keycloak-js appending session data to url in vue3 - url

keycloak-js appears to be appending session data when I refresh my vue3 application: https://my.domain/#/&state={state}&session={session} etc
I wouldn't have a problem with this except it's breaking my site when I refresh due to the incorrect url format.
I can't find where this appears to be trying to append the data in the url from.

Here is a workaround to configure in router configuration. Credits from this thread.
const removeKeycloakStateQuery = (to, from) => {
const cleanPath = to.path
.replace(/[&\?]code=[^&\$]*/, "")
.replace(/[&\?]state=[^&\$]*/, "")
.replace(/[&\?]session_state=[^&\$]*/, "");
return { path: cleanPath, query: {}, hash: to.hash };
};
// ...
{
path: "/:catchAll(.*)*",
component: () => import("src/pages/component.vue"),
beforeEnter: [removeKeycloakStateQuery],
}

It ended up being that keycloak-js appears to conflict with createWebHashHistory in vue-router. I've updated it to just use createWebHistory and now my site is working.
There is logic in keycloak-js that tries to determine if you're in a query string and I found the problem by stepping through that code in parseCallbackUrl.
Example: As stated, I ended up using createWebHistory instead of createWebHashHistory. I made the change in my router/index.js file in the createRouter method passing the history option:
import { createRouter, createWebHistory } from 'vue-router'
/* Other router code here */
const router = createRouter({
history: createWebHistory(), // was createWebHashHistory() with matching import substitution
routes
})
export default router

Related

In electron how to send custom header and value for every request?

I use electronjs for building a cross platform desktop application. I would like to send a custom header with a value for every request from electron. Initially in loadURL(), i could use extraHeaders to set the custom header. How to send it in all subsequent requests?
As recommended by the documentation, you should use session object and the method onBeforeSendHeaders:
const { session } = require('electron')
// Modify the user agent for all requests to the following urls.
const filter = {
urls: ['https://*.github.com/*', '*://electron.github.io']
}
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent'
callback({ requestHeaders: details.requestHeaders })
})

Pagination - How can I store and provide a non-numeric page identifier?

I am trying to add pagination to my Zapier trigger.
The API I am using for the trigger supports pagination, but not using a page number in the traditional sense (ie. page 1,2,3,...). Instead, the API response includes a key (ie. "q1w2e3r4") which should be passed as a parameter to the next request to get the next page of results.
From looking at the docs, I can use {{bundle.meta.page}} (which defaults to 0 unless otherwise set).
I am trying to set {{bundle.meta.page}} in the code editor, with an example shown below:
const options = {
url: 'company_xyz.com/api/widgets',
method: 'GET',
...,
params: {
...,
'pagination_key': bundle.meta.page,
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const json_response = response.json;
widgets = json_response.widgets
...
bundle.meta.page = json_response["next_pagination_key"]
return widgets;
});
The problem is that when Zapier tries to retrieve the next page, bundle.meta.page will be 1 instead of the value of "next_pagination_key" from the result of the previous request.
There are docs on cursor-based pagination in the CLI docs.
The relevant block is:
const performWithAsync = async (z, bundle) => {
let cursor;
if (bundle.meta.page) {
cursor = await z.cursor.get(); // string | null
}
const response = await z.request(
'https://5ae7ad3547436a00143e104d.mockapi.io/api/recipes',
{
// if cursor is null, it's sent as an empty query
// param and should be ignored by the server
params: { cursor: cursor }
}
);
// we successfully got page 1, should store the cursor in case the user wants page 2
await z.cursor.set(response.nextPage);
return response.items;
};
This should work in the Zapier Visual Builder, but you might need to use the CLI instead. You can export your integration using the zapier convert CLI command (docs).

cy.url() and/or cy.location('href') does not return a string

I have an editor page. When I add any content and click the "Save" button my URL will change, adding a random id in the URL. I want to check if my ID's are changing every time when I click the "Save button".
I save the URL result in variable and want to check it, I do it like this:
const currentURL = cy.url();
cy.get('.editor-toolbar-actions-save').click();
cy.url().should('not.eq', currentURL);
But my currentURL variable's type is not string:
expected http://localhost:8080/editor/37b44d4d-48b7-4d19-b3de-56b38fc9f951 to not equal { Object (chainerId, firstCall) }
How I can use my variable?
tl;dr
Cypress commands are asynchronous, you have to use then to work with their yields.
cy.url().then(url => {
cy.get('.editor-toolbar-actions-save').click();
cy.url().should('not.eq', url);
});
Explanation
A similar question was asked on GitHub, and the official document on aliases explains this phenomenon in great detail:
You cannot assign or work with the return values of any Cypress command. Commands are enqueued and run asynchronously.
The solution is shown too:
To access what each Cypress command yields you use .then().
cy.get('button').then(($btn) => {
// $btn is the object that the previous
// command yielded us
})
It is also a good idea to check out the core concepts docs's section on asynchronicity.
These commands return a chainable type, not primitive values like strings, so assigning them to variables will require further action to 'extract' the string.
In order to get the url string, you need to do
cy.url().then(urlString => //do whatever)
I have been having the same issue and so far most consistent method has been to save the URL to file and read it from file when you need to access it again:
//store the url into a file so that we can read it again elsewhere
cy.url().then(url => {
const saveLocation = `cypress/results/data/${Cypress.spec.name}.location.txt`
cy.writeFile(saveLocation, getUrl)
})
//elsewhere read the file and do thing with it
cy.readFile(`cypress/results/data/${Cypress.spec.name}.location.txt`).then((url) => {
cy.log(`returning back to editor ${url}`)
cy.visit(url)
})
Try this:
describe("Test Suite", () => {
let savedUrl;
beforeEach(() => {
cy.visit("https://duckduckgo.com/");
cy.url().then(($url) => {
savedUrl = $url;
});
});
it("Assert that theURL after the search doens't equal the URL before.", () => {
cy.get("#search_form_input_homepage").type("duck");
cy.get("#search_button_homepage").click();
// Check if this URL "https://duckduckgo.com/?q=duck&t=h_&ia=web"
// doesn't equal the saved URL "https://duckduckgo.com/"
cy.url().should("not.eq", savedUrl);
});
});
Refer below code snippet, Here you can get the current URL and store it in a variable, do print via cy.log()
context('Get Current URL', () => {
it('Get current url and print', () => {
cy.visit('https://docs.cypress.io/api/commands/url')
cy.url().then(url => {
const getUrl = url
cy.log('Current URL is : '+getUrl)
})
})
})
#Max thanks this helped to get some ideas on different versions.
The way I did it is:
Create a .json file in your fixtures folder (name it whatever you want).
On the new .json file, only add: { } brackets and leave the rest blank. The function will self populate that .json file.
Create a new function on the commands page to easily call it on your test.
It would probably be best to create two functions, 1 function to write url or the sliced piece of the url, and the another function to call it so you can use it.
A. Example of 1st method, this method cuts the id off of the URL and stores it on the .json file:
Cypress.Commands.add('writeToJSON', (nameOfJSONSlicedSection) =>
{
cy.url().then(urlID =>
{
let urlBit = urlID.slice(urlID.indexOf('s/') + 2, urlID.indexOf('/edit'))
cy.writeFile('cypress/fixtures/XYZ.json', {name: nameOfJSONSlicedSection, id: urlBit}) /*{ }<-- these will populate the json file with name: xxxxx and id:xxxxx, you can changes those to whatever meets your requirements. using .slice() to take a section of the url. I needed the id that is on the url, so I cut only that section out and put it on the json file.*/
})
})
B. 2nd example function of calling it to be used.
This function is to type in the id that is on the url into a search box, to find the item I require on a different it() block.
Cypress.Commands.add('readJSONFile', (storedJSONFile) =>
{
cy.readFile('cypress/fixtures/XYZ.json').its('id').then((urlSetter) => {
cy.log(storedJSONFile, 'returning ID: ' + urlSetter)
//Search for Story
cy.get('Search text box').should('be.visible').type(urlSetter, {delay: 75})
})
})
/*here I use a .then() and hold the "id" with "urlSetter", then I type it in the search box to find it by the id that is in the URL. Also note that using ".its()" you can call any part section you require, example: .its('name') or .its('id') */
I hope this helps!

Remove all URL query parameters with normalize-url

I'm trying to remove all query parameters from a URL with normalize-url package but getting some strange results.
I'm using the removeAllQueryParameters option as follows:
if (options.removeAllQueryParameters) {
for (const key of urlObj.searchParams.keys()) {
urlObj.searchParams.delete(key);
}
}
And using it as follows when calling the method to add the URL to the database:
{
let url = normalizeUrl(model.article.url,{removeAllQueryParameters: true});
callServerMethod({
name: 'addNewPost',
data: {
title: model.article.title,
url: url,
},
For the example URL: https://example.com?utm_source=test&utm_medium=test&utm_campaign=test
I'm getting the following result:
https://example.com?utm_campaign=test
Interestingly for the second example URL: https://example.com?utm_source=test
The result is https://example.com - correct.
The function is skipping the last parameter in a situation where there are more than 1 parameters to be removed.
Thanks in advance for any hints.
searchParams.keys() returns Iterator and it's bad idea to modify the source object while iterating it.
Simply make copy of keys into an array and iterate over it instead:
for (const key of [...urlObj.searchParams.keys()]) {
urlObj.searchParams.delete(key);
}
The following approach worked for me, however, I'm not sure if setting the urlObj.search like that is a good practice:
if (options.removeAllQueryParameters) {
urlObj.search = '';
}

Invalidate Falcor jsonGraph fragment using jsonGraphEnvelope

I'm trying to invalidate a part of my jsonGraph object via the response from the falcor-router after making a CREATE call. I can successfully do so when returning a list of pathValues, similar to this earlier SE question:
{
route: 'foldersById[{keys:ids}].folders.createSubFolder',
call(callPath, args, refPaths, thisPaths) {
return createNewFolderSomehow(...)
.subscribe(folder => {
const folderPathValue = {
path: ['foldersById', folder.parentId, 'folders', folder.parentSubFolderCount -1],
value: $ref(['foldersById', folder.id])
};
const folderCollectionLengthPathValue = {
path: ['folderList', 'length'],
invalidated: true
};
return [folderPathValue, folderCollectionLengthPathValue];
});
})
}
However, when returning the equivalent (afaik) jsonGraphEnvelope, the invalidated path is dropped from the response:
{
route: 'foldersById[{keys:ids}].folders.createSubFolder',
call(callPath, args, refPaths, thisPaths) {
return createNewFolderSomehow(...)
.subscribe(folder => {
const newFolderPath = ['foldersById', folder.parentId, 'folders', folder.parentSubFolderCount -1];
return {
jsonGraph: R.assocPath(folderPath, $ref(['foldersById', folder.id]), {})
paths: [newFolderPath],
invalidated: [['folderList', 'length']]
};
});
})
}
Am I misunderstanding how a jsonGraphEnvelope works (had assumed it was a longhand format equivalent to an array of PathValues)? Or is this likely a bug?
Looks like a bug to me.
Invalidations don't seem to be handled in the part of the code responsible for merging partial JSONGraph envelopes returned from routes into the JSONGraph envelope response (see here), while they are handled in the path-value merge (see here).
I can't find any issue about this on GitHub so I invite you to open one.

Resources