Redux Toolkit Query and pre fetching some datas - javascript

I am using Redux Toolkit Query to fetch datas from Audius server. The service is based on many IPFS nodes, and it is best practice to make a query of the best performing servers to which send the API requests in that particular moment. This is the function Audius API docs suggest to use in order to find the right server:
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)]
var host = await fetch('https://api.audius.co')
.then(r => r.json())
.then(j => j.data)
.then(d => sample(d))
I need to get the url from this function and feed it into the function where I use the createApi method. I wrapped the function in an async function, since it uses await, but I am stuck in the Promise.
async function getHost() {
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)]
var host = await fetch('https://api.audius.co')
.then(r => r.json())
.then(j => j.data)
.then(d => sample(d))
}
I don't know how to get a value as result of this function, I know how to do it with React components, using useState Hook, but I would not like to use it here, as I think it will slow down the process. I tried to understand how to use the createAsyncThunk but I can't wrap my mind around it.
The rest of the code looks like this:
const contentProvider = `https://discoveryprovider2.audius.co`
const audiusVersion = `/v1`
const appName = `app_name=ZION`
const baseUrl = contentProvider + audiusVersion
const section = [`/users`, `/playlists`, `/tracks`]
const search = `/search?query=`
export const audiusApi = createApi({
reducerPath: 'audiusApi',
baseQuery: fetchBaseQuery({ baseUrl: `${baseUrl}` }),
endpoints: (builder) => ({
// SEARCH USERS
// https://discovery-a.mainnet.audius.radar.tech/v1/users/search?query=Brownies&app_name=EXAMPLEAPP
searchUsers: builder.query({
query: (searchQuery) => `${section[0]}${search}${searchQuery}${appName}`
}),
.........})
}),
})
export const {
useSearchUsersQuery,
useGetUserQuery,
useGetUsersFavTracksQuery,
useGetUsersRepostsQuery,
useGetUserMostUsedTagsQuery,
useGetUserTracksQuery,
useSearchPlaylistQuery,
useGetPlaylistQuery,
useGetPlaylistTracksQuery,
useSearchTracksQuery,
useTrendingTracksQuery,
useGetTrackQuery,
useStreamTrackQuery
} = audiusApi
So basically I need to place the result of the async function = to contentProvider.
I tried doing simply
var response = getHost() // my async function
var contentProvider = response
but this doesn't pass through
Hope someone can help me out with this one =).

So you want a baseQuery with a dynamic baseUrl.
We have an example on a baseQuery that uses Redux state for the baseUrl in the docs.
That still means, you have to get the baseUrl into the store though.
Adjusting an example from the Redux Essentials tutorial:
export const getHost = createAsyncThunk('host/getHost', async () => {
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)]
return fetch('https://api.audius.co')
.then(r => r.json())
.then(j => j.data)
.then(d => sample(d))
})
const hostSlice = createSlice({
name: 'host',
initialState: null,
reducers: {
},
extraReducers(builder){
builder.addCase(getHost.fulfilled, (state, action) => {
return action.payload
}
}
})
Then you plug your hostSlice.reducer into configureStore:
const store = configureStore({
reducer: {
host: hostSlice.reducer
// more stuff you had before
}
})
and dispatch the thunk:
dispatch(getHost())
Your host will be available via getState().host in your adjusted baseQuery then.

if I understand you well, I think you struggle with how to work with async functions.
you can easily use async await.
async function getHost() {
const sample = (arr) => arr[Math.floor(Math.random() * arr.length)]
var host = await fetch('https://api.audius.co');
var json = await host.json();
var data = await json.data;
var d = await sample(data);
return d;
}
and you can call it as
var result = await getHost();
result.
then(d => console.log(d));

Related

async axios get call axios returns nothing

I'm trying to get the current temperature from openweathermaps using axios.
I can put my url into the browser and it works fine. But when I try do an axios call with the correct url it doesnt event call.
Here is the relevant code section:
function Overview() {
const [temperature, setTemperature] = useState('')
const API_KEY = '{apikey}'
const getTemperature = useCallback(async () => {
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude.toFixed(2)}&lon=${longitude.toFixed(
2,
)}&appid=${API_KEY}`
console.log('my provided url is set to:', url)
const response = await axios.get(url)
if (response.data) {
setTemperature(response.data.main.temp)
}
}, [latitude, longitude])
useEffect(() => {
getTemperature().catch(console.error)
}, [getTemperature])
return <>{temperature ? temperature : 'no data'}</>
}
Any help as to where I'm going wrong would be great as I just cant see my error!
Your URL working on the browser showcases that your API key is correct. Now, make sure the variables in your URL are properly set from the code, particularly the API key. you can console.log them to be sure. Passed that, the minimal code below would do.
import {useCallback, useEffect} from 'react';
import axios from 'axios';
function Overview() {
// YOUR_INITIAL_STATE rather be undefined
const [temperature, setTemperature] = useState(YOUR_INITIAL_STATE);
const API_KEY = 'YOUR_API_KEY';
const getTemperature = useCallback(async () => {
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`;
console.log("my provided url is set to:", url);
const response = await axios.get(url);
if(response.data){
setTemperature(response.data.main.temp);
}
// only call the functtion when these deps change
}, [latitude, longitude])
useEffect(() => {
// Check the error for further debbugging
getTemperature()
.catch(console.error);
}, [getTemperature])
return (
<>
{temperature ? temperature : "no data"}
</>
);
}
It looks like your arrow function is calling itself recursively:
const setTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
setTemperature(response.data.main.temp);
};
Your code doesn't show where this is being called. Probably it needs to be something like:
const setTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
return response.data.main.temp;
};
Perhaps it just needs to be a one letter change:
const getTemperature = async () => {
const response = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}`);
setTemperature(response.data.main.temp);
};
Credit to #devklick

Playwright - sharing state between tests

I'm learning Playwright and JavaScript concurrently so this may be an elementary question - I'm wondering how people would recommend sharing state - variable customerId in this case - between tests.
Example:
test.describe.only('Generate a new customer', () => {
let customerId
let baseUrl = process.env.SHOP_URL
test('Create new customer', async ({ request }) => {
const response = await request.post(baseUrl + `/shopify/v5/customer`, {})
const responseBody = JSON.parse(await response.text())
expect(response.status()).toBe(200)
customerId = responseBody.customerId //need to persist customerId to pass into following test
})
test('Update customer details', async ({ request }) => {
const response = await request.post(baseUrl + `/shopify/v5/customer/update`, {})
{
data: {
customerId: customerId, //customerId is undefined here
name: "Fred"
},
}
)
expect(response.status()).toBe(200)
})
the customerId is clearly out of scope in the second test. I will probably refactor these to use a library such as Axios eventually because I am using the Playwright tests to generate data - I'm not actually testing the api here. In the meantime I just need customerId to be persisted in subsequent api calls.
To make your example work you need to run the tests in serial mode, something like this will work:
test.describe.serial('Generate a new customer', () => {
let customerId
let baseUrl = process.env.SHOP_URL
test('Create new customer', async ({ request }) => {
const response = await request.post(baseUrl + `/shopify/v5/customer`, {})
const responseBody = JSON.parse(await response.text())
expect(response.status()).toBe(200)
customerId = responseBody.customerId //need to persist customerId to pass into following test
})
test('Update customer details', async ({ request }) => {
const response = await request.post(baseUrl + `/shopify/v5/customer/update`, {})
{
data: {
customerId: customerId, //customerId is undefined here
name: "Fred"
},
}
)
expect(response.status()).toBe(200)
})
});
That is anti-pattern, tests should be independent especially in playwright where tests run in parallel by default:
https://playwright.dev/docs/test-parallel
You can merge those two tests into one test.
If You still want to go that way I guess You can use fixtures or hooks to make it work, here are examples:
https://playwright.dev/docs/test-fixtures#without-fixtures

How can I access Firestore data from within a Google Cloud Function?

This is my first time using Cloud Functions. I'm trying to make a simple call to access all the businesses stored in my Firestore collection, but when I try to log the results, I always get an empty array.
All things w/ Firebase/store are set up properly, collection name is listed properly, and have confirmed access to the database by logging db. Is there something obviously wrong with my code here? Thanks!
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.updateBusinessData = functions.https.onRequest((request, response) => {
const db = admin.firestore()
const businessesReference = db.collection('businesses')
var businesses = []
const getBusinesses = async () => {
const businessData = await businessesReference.get()
businesses = [...businessData.docs.map(doc => ({...doc.data()}))]
for (let business in businesses) {
console.log(business)
}
response.send("Businesses Updated")
}
getBusinesses()
});
I tweaked the way you were processing the docs and I'm getting proper data from firestore.
exports.updateBusinessData = functions.https.onRequest((request, response) => {
const db = admin.firestore();
const businessesReference = db.collection("businesses");
const businesses = [];
const getBusinesses = async () => {
const businessData = await businessesReference.get();
businessData.forEach((item)=> {
businesses.push({
id: item.id,
...item.data(),
});
});
// used for of instead of in
for (const business of businesses) {
console.log(business);
}
response.send(businesses);
};
getBusinesses();
});
Your getBusinesses function is async: you then need to call it with await. Then, since you use await in the Cloud Function you need to declare it async.
The following should do the trick (untested):
exports.updateBusinessData = functions.https.onRequest(async (request, response) => {
try {
const db = admin.firestore()
const businessesReference = db.collection('businesses')
var businesses = []
const getBusinesses = async () => {
const businessData = await businessesReference.get()
businesses = businessData.docs.map(doc => doc.data());
for (let business in businesses) {
console.log(business)
}
}
await getBusinesses();
// Send back the response only when all the asynchronous
// work is done => This is why we use await above
response.send("Businesses Updated")
} catch (error) {
response.status(500).send(error);
}
});
You are probably going to update docs in the for (let business in businesses) loop to use await. Change it to a for … of loop instead as follows:
for (const business of businesses) {
await db.collection('businesses').doc(...business...).update(...);
}
Update following the comments
Can you try with this one and share what you get from the console.logs?
exports.updateBusinessData = functions.https.onRequest(async (request, response) => {
try {
console.log("Function started");
const db = admin.firestore()
const businessesReference = db.collection('businesses');
const businessData = await businessesReference.get();
console.log("Snapshot size = " + businessData.size);
const businesses = businessData.docs.map(doc => doc.data());
for (const business of businesses) {
console.log(business);
}
response.send("Businesses Updated")
} catch (error) {
console.log(error);
response.status(500).send(error);
}
});

rxjs process ajax request

I'm trying to apply the ajax method posted here: https://github.com/redux-observable/redux-observable/blob/master/docs/basics/Epics.md
import { ajax } from 'rxjs/ajax';
// action creators
const fetchUser = username => ({ type: FETCH_USER, payload: username });
const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload });
// epic
const fetchUserEpic = action$ => action$.pipe(
ofType(FETCH_USER),
mergeMap(action =>
ajax.getJSON(`https://api.github.com/users/${action.payload}`).pipe(
map(response => fetchUserFulfilled(response))
)
)
);
// later...
dispatch(fetchUser('torvalds'));
When trying this method, I get the message:
TypeError: Object(...)(...).pipe is not a function
So pipe doesn't appear to exist. (It concerns the second pipe after the ajax call).
How do I fix this?
I installed the following dependencies:
"rxjs": "^6.5.2",
"rxjs-compat": "^6.5.2",
Edit:
I changed my code to ajax.get and the calling code:
export const fetchTrendingEpic = action$ => action$.pipe(
ofType(FETCH_TRENDING_REQUEST),
mergeMap(async(action) => {
const res = await fetchPostStats(action.payload);
console.log(res);
res.pipe(
map(response => {
console.log('response', response);
setTrendingPlaces({trendingPlaces: response});
})
)
})
);
The res was properly printed (showing an observable), but now I get an error:
TypeError: Cannot read property 'type' of undefined
This is how I create my store in dev:
const createEnhancer = (epicMiddleware) => {
const middleware = [ epicMiddleware, createLogger() ];
let enhancer;
if (getEnvironment() === 'development') {
enhancer = composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
);
};
export default (initialState) => {
const epicMiddleware = createEpicMiddleware();
const enhancer = createEnhancer(epicMiddleware);
const store = createStore(rootReducer, initialState, enhancer);
epicMiddleware.run(rootEpic);
return store;
}
Edit note:
This is code executed in NodeJS (SSR).
I'm struggling with this, don't really understand how this can be so hard to get working without error.
Can't quite see how the example code will ever work when ajax.getJSON returns a promise, not an Observable...
My service that called the ajax request was marked with async because the previous implementation used Axios before the refactoring.
This was the reason why my console logged a promise as return value of the function.
Removing async returns the Observable as expected.
As #Dez mentioned in the comments, there is also need to add return values.
And last but not least, rxjs ajax does not work in a NodeJS environment.
Therefore, based on this thread:
https://github.com/ReactiveX/rxjs/issues/2099
I have found that someone created the following npm package that I will try out shortly: https://github.com/mcmunder/universal-rxjs-ajax
Axios works fine with rxjs in nodejs, just doing something like this...
const { from } = require('rxjs');
const { map } = require('rxjs/operators');
const axios = require('axios');
const responsePromise = axios.get('https://jsonplaceholder.typicode.com/todos/1');
const response$ = from(responsePromise);
response$
.pipe(
map(response => ({ type: 'RESPONSE_RECEIVED', payload: response.data}))
)
.subscribe(console.log);

Firebase Firestore: How to get ref from snapshot via using async/await

I am using Cloud Firestore in Firebase functions with Node.js 8
Simple open question is: Does it possible to get ref from .get() via using async/await?
Example:
const snapshot = await db.collection(/*..*/).doc(/*..*/).get();
const data = snapshot.data();
const ref = /* ???? */
// Then using...
ref.update({/*..*/});
or should I just do like?
const ref = db.collection(/*..*/).doc(/*..*/);
const snapshot = await ref.get();
/* so on.../*
If you are trying to get a new reference from your snapshot constant then its possible
I would so it this way
example
const areaSnapshot = await admin.firestore().doc("areas/greater-boston").get()
const bostonCities = areaSnapshot.data().cities;
const allAreas = await areaSnapshot.ref.parent.doc("new-york").get()
const nyCities= allAreas.data().cities
console.log(bostonCities, nyCities)
update document
//to update document
const areaSnapshot = await admin.firestore().doc("areas/greater-boston").get()
const allAreas = areaSnapshot.ref.parent.doc("new-york").update({
capital: {
liberty: true
}
})
await allAreas
.then(() => {
console.log("success")
})
.catch(err => console.log(err))
Source:
https://firebase.google.com/docs/firestore/manage-data/add-data

Categories

Resources