I use the redux toolkit to create an API
The question is short: how to dynamically change the baseUrl in the API?
The question in detail:
Here is the createApi method
An object is passed to this method, one of the keys of which is "baseQuery"
export const WebApi = createApi({
reducerPath: 'API',
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:3001/api' }),
endpoints: () => ({}),
});
And here's the question, how do I dynamically change the baseUrl? It is in the store, but I can't just put it here.
I tried the solution from the customization query documentation
https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#constructing-a-dynamic-base-url-using-redux-state
But it does not allow to change exactly the baseUrl, only dynamically process the request itself, which already comes AFTER the baseUrl
So, how can I get and change baseUrl in the createApi method?
So, the answer is:
right in the file where you create api
past code below:
const dynamicBaseQuery: BaseQueryFn<string | FetchArgs,
unknown,
FetchBaseQueryError> = async (args, WebApi, extraOptions) => {
const baseUrl = (WebApi.getState() as any).configuration.baseUrl;
const rawBaseQuery = fetchBaseQuery({ baseUrl });
return rawBaseQuery(args, WebApi, extraOptions);
};
I use baseUrl from store, because my config already in it. But you can make an async await fetch here, to get data from any place
and this constant, dynamicBaseQuery insert into baseQuery key in your createApi
export const WebApi = createApi({
reducerPath: 'API',
baseQuery: dynamicBaseQuery,
endpoints: () => ({}),
});
If you pass in a full url (starting with http:// or https://) fetchBaseQuery will skip baseUrl and use the supplied url instead. So you can use the logic that you linked above to just prepend your current baseUrl to the url of the query.
Related
I have a StencilJS app with Ionic web components using the following routing setup:
<ion-router useHash={false}>
<ion-route url="/home" component="page-home"/>
<ion-route url="/login" component="page-login"/>
<ion-route url="/main" component="page-main">
</ion-router>
When I receive a 401 error from the api, I would like to redirect to the login page, with the current url as a redirect querystring parameter (in order to redirect the user back to where he came from), like so:
const navCtrl: HTMLIonRouterElement = document.querySelector("ion-router");
fetch('https://someapi.com/users/1').then(
res => navCtrl.push('/main'),
err => navCtrl.push(`/login?redirect=${window.location.pathname}`)
);
When I do this an I receive an error from the api, and I want to navigate to the login page with the current route as querystring parameter, Stencil throws the following error: [ion-router] the path does not match any route.
How can I use the querystring with the ion-router?
IMO it's currently a bug in Ionic (see https://github.com/ionic-team/ionic/issues/19707).
To work around it for the moment, you could probably use
navCtrl.push('/login');
history.replaceState(undefined, '', `?redirect=${window.location.pathname}`);
Thanks for the nudge in the right direction! The code you provided did not change the url, because navCtr.push returns a promise. You have to wait for it to finish before you can change the state. I ended up with this working workaround:
const navCtrl: HTMLIonRouterElement = document.querySelector("ion-router");
fetch('https://someapi.com/users/1').then(
res => navCtrl.push('/main'),
err => {
navCtrl
.push(`/login`)
.then(() => history.replaceState(undefined, '', `?redirect=${encodeURIComponent(window.location.pathname)}`))
}
);
It's strange that such widely used frameworks like Ionic and Stencil don't support common querystrings. I hope they will fix this issue soon.
i build a native function to get every parameter from the querystring,
export const RouterGetUriParam = (name) => {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has(name)) {
return urlParams.get(name);
} else {
return null;
}
};
you can use it in stencil like this:
#Prop() withMore: boolean = (RouterGetUriParam("withMore") == 'true')
the urlparam looks like ?withMore=true
I'm using the account linking feature for Actions SDK and following the guide here (https://developers.google.com/assistant/identity/google-sign-in#start_the_authentication_flow)
It shows the initialization like this
const app = actionssdk({
// REPLACE THE PLACEHOLDER WITH THE CLIENT_ID OF YOUR ACTIONS PROJECT
clientId: CLIENT_ID,
});
But for my use case, I'll read the clientId from DB which is stored against the projectId of the project. I can extract the projectId only after the MAIN intent is triggered.
My question is, how can I set the clientId after initializing actionssdk?
This solution uses the new Actions SDK, but the principal is the same for the legacy SDK as well:
const {
conversation,
Canvas,
} = require('#assistant/conversation');
const functions = require('firebase-functions');
const wrapper = async (req, res) => {
// You can get any data you need here:
const myAsyncBootstrapData = await getData();
const app = conversation({debug: true, ...myAsyncBootstrapData});
app.handle('welcome', (conv) => {
conv.add('This is a demo.');
});
return app(req, res);
};
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(wrapper);
functions.https.onRequest accepts any callable, including ones that return promises. If you need to block while loading configuration data asynchronously, you can do so by wrapping your definition in an async function.
I found a simple solution to this. I am adding it here for future references.
// handler.js
async function handleRequest(req, res) {
const clientId = // retrieve the clienId using your business logic
const app = actionssdk({
clientId: clientId
})
}
module.exports = handleRequest;
Instead of directly creating an instance of actionssdk, wrap it inside a function like this.
// index.js
const handler = require('./path/to/hander.js');
app.post('/webhook', handler);
Then when defining the webhook, use the wrapper function to handle the webhook requests
how can I access URL parameters from Azure DevOps Extension?
I am developing a hub-like extension based on this example. Basically it is a simple page that displays data in a simple table. Data are loaded from an external REST API server.
I want to call this page from some external link and for this, I need to read URL parameter idBuild from this extension. Is this possible?
Example on my URL: http://server/tfs/org/proj/_apps/hub/devops-plugin.default-hub?idBuild=15987
Edit: More details about this plugin:
For Pull requests, my Pull Request server posts Pull request status with the information about some additional checks (x86 integration tests here).
I want this Status to have a URL, so a user can display some additional information about this status on a separate page. This page is my extension.
In this extension, I read some data from an external API and idBuild is the key. So I want to make URL containing idBuild and pass idBuild to this extension page through this URL.
Some months ago I faced the same problem as you have now.
While using aurelia / typescript I was not able to read the URL parameters, but I found a workaround to retrieve them.
Use the following function to get all the parameters:
function getUrlParameters() {
var parameters = {};
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, (m, key, value) => {
parameters[key] = value.split("#")[0];
return parameters[key];
});
return parameters;
}
And this piece of code to get the idBuild parameter:
var buildId = getUrlParameters()["idBuild"];
Thanks to R Pelzer answer, here is TypeScript version of his function:
private getUrlParameters(): Map<string, string> {
const parameters: Map<string, string> = new Map<string, string>();
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, (m, key: string, value: string) => {
const val = value.split("#")[0];
parameters.set(key, val);
return val;
});
return parameters;
}
And here is a usage example:
public async componentDidMount() {
const idBuildParam = this.getUrlParameters().get("idBuild");
if (!idBuildParam)
{
throw RangeError("Missing 'idBuild' URL parameter");
}
const response = await axios.get<PoirotResultDto[]>(`http://localhost:50584/Poirots/${idBuildParam}`);
.... rest of code
}
I need to add credentials: 'same-origin' to all fetch requests in order to make a PWA work in a password-protected website. Otherwise if I leave the website and come back later, the browser doesn't ask for my password and returns an Unauthorized error.
How do I do that with Workbox ? I noticed the RequestWrapper class includes fetchOptions, but I can't find a way to use them.
In v2, precaching should already set credentials: 'same-origin' as the FetchOptions on all outgoing requests.
For runtime caching, you can get this behavior by constructing your own RequestWrapper instance and passing that in to the runtime caching handler you're using:
const requestWrapper = new RequestWrapper({
cacheName: 'my-cache-name', // Change this for each separate cache.
fetchOptions: {
credentials: 'same-origin',
},
plugins: [], // Add any plugins you need here, e.g. CacheExpiration.
});
const staleWhileRevalidateHandler = new StaleWhileRevalidate({requestWrapper});
workboxSW.router.registerRoute(
new RegExp('/path/prefix'),
staleWhileRevalidateHandler
);
(I'm not sure how you're using the Workbox libraries, but you may need to explicitly import additional scripts to get access to the RequestWrapper class, like https://unpkg.com/workbox-runtime-caching#2.0.3/build/importScripts/workbox-runtime-caching.prod.v2.0.3.js)
Thanks to Jess Posnick for the answer.
To avoid rewriting all workbox strategies, I ended up using a custom plugin:
const addFetchOptionsPlugin = {
requestWillFetch: ({ request }) => new Request(request, {
credentials: 'same-origin', redirect: 'follow'
})
};
workbox.router.registerRoute(
new RegExp('my-route'),
workbox.strategies.cacheFirst({
cacheName: 'my-cache',
plugins: [addFetchOptionsPlugin]
})
);
I want to redirect from a route, /new, and keep the query params for the new route:
As far as I know, the only place to access queryParams is within the model hook of a route.
But I want to redirect in the beforeModel hook:
import Ember from "ember";
export default Ember.Route.extend({
/**
* ##override
* Implicitly create a new applicant, redirecting to the generated ID at /applications/#{id}
* #param transition
*/
beforeModel: function(transition) {
var emptyApplicant = this.store.createRecord("applicant",
{isPrimary: true}
),
emptyApplication = this.store.createRecord("application");
emptyApplication.set("applicant", emptyApplicant);
emptyApplicant.save().then((savedApplicant) => {
emptyApplication.save().then((savedApplication) => {
this.transitionTo("applications", savedApplication);
});
});
}
});
While the above code works, the transition will complete without preserving the query params. For example, navigating to applicants/new?agent=35 will not preserve agent=35 in the query param, and will simply redirect to applicants/new instead.
How can I access the queryParams object from the beforeModel hook in my Ember app?
You should be able to pass query parameters to the transitionTo, something along the line of:
this.transitionTo("applications", savedApplication, {
queryParams: transition.queryParams
});
In more modern versions of Ember, the transition object has a to property which contains the query params. Transition no longer has a queryParams property
A redirect to the index route in a beforeModel for example, might look like this:
beforeModel(transition){
super.beforeModel(...arguments);
transition.abort();
this.transitionTo('index', {
queryParams: transition.to.queryParams
});
}