Modify deeply nested data in Apollo client query result - javascript

I'm using #apollo/client to fetch data from Storyblok's API.
I want to iterate over all the fields in the returned data to potentially sanitize its content (it contains raw HTML). Because Apollo makes the result data immutable, I'm not able to do this:
const client = new ApolloClient({
uri: "https://gapi.storyblok.com/v1/api",
cache: new InMemoryCache(),
headers: {
token: "EXAMPLE",
},
});
function sanitizeFields(obj) {
for (const field of obj) {
if (field?.type === "custom-rich-text") {
field.content = sanitizeContent(field.content); // 💥 This generates an error because I'm trying to modify a read only object
}
if (Array.isArray(field) || isPlainObject(field)) {
sanitizeFields(field);
}
}
}
const postData = await client.query({ query: MY_QUERY });
sanitizeFields(postData?.data?.Page);
How can I modify deeply nested data returned by #apollo/client without knowing the particular field name beforehand?

Related

How do I stream Product objects in object mode with the fetch API

I am processing data from a server which returns JSON objects. I read that it is possible to process data in different modes, byob and object mode. Unfortunately, I cannot seem to find any examples demonstrating that scenario. I don't want to have to read a byte buffer, decode and then process the result JSON if there is a way to get the chunks as objects instead? I can see the data in my browser but when I assign it to a Product object in my UI, no products show. Here is my sample code:
interface Product {
productId: string,
productName: string,
barcode: string,
quantityOnHold: number
}
Here is how I am attempting to use the Fetch API. I'd like to use the highWaterMark that I read about but don't know how to use to return objects.
const retrieveProducts = async () => {
const products = [];
const productBody ={
category: 'fragrance',
productCodes: [122222, 555555, 444444444]
}
const response = await fetch('/products', {
method: 'POST',
body: JSON.stringify(productBody)
})
const readableStream = response.body.pipeThrough(new TextDecoderStream());
readableStream.pipeTo(new WritableStream({
write(chunk) {
console.log("Chunk received", chunk);
products.push(JSON.parse(chunk)); // does not work!!
// I want to be able add products
// i.e. products.push(....)
},
close() {
console.log("All products successfully processed!");
},
abort(e) {
console.error("An error occurred!", e);
}
}));
}

How to get nested objects using Firestore REST API?

I'm sending REST API requests using axios package.
I can get a single document (for example, cities/cityId):
axios.get(`https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>/<DOCID>`)
What I can't do is to get a nested document (for example, cities/cityId/streetId)
The database structure is very simple.
cities: { // COLLECTION
cityId: { // DOCUMENT
streetId: { // MAP
buildingId: '...', // STRING
...
},
...
},
}
This article Google Firestore REST API examples suggests that it's possible to get nested objects using structured queries. Unfortunately, I've been trying to do it without any success.
Here's my not working code:
getQuery ({ path, token }) {
const url = 'https://firestore.googleapis.com/v1/projects/big-boobs/databases/(default)/documents:runQuery'
const params = {
from: [ { collectionId: 'cities' } ],
select: {
fields: [
{ fieldPath: 'cityId1.streetId' }
]
}
}
const options = {
method: 'get',
url,
params,
paramsSerializer: function (params) {
return Qs.stringify(params, { arrayFormat: 'brackets' })
}
}
if (token) options.headers['Authorization'] = `Bearer ${token}`
return axios(options)
}
I'm getting an error:
{
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"from[][collectionId]\": Cannot bind query parameter. Field 'from[][collectionId]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"select[fields][][fieldPath]\": Cannot bind query parameter. Field 'select[fields][][fieldPath]' could not be found in request message.",
}
You can select individual fields from a document, but you can't select individual properties from a map field. I think you'll have to settle for getting the entire map called streetId.
If it's unacceptable to pull down the entire map, you can reorganize your data such that each streetId exists in its own document.

Sequelizejs - Then result issue

I'm work with NodeJS and Sequelize. I have the following issue:
Read the Settings Table:
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Next request
});
I need to save the settings.device (Example) outside of the .then block.
But if I do that like
var device;
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
device = settings.device;
});
It doesn't work.
Already the error output undefined
The output in .then result block with console.log(settings.device); works perfect.
Update
I need it like:
var array = [];
// Get Settings from table
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Get token from other Table
Example.findOne({where: {
user_id: data
}})
.then(example => {
// push to array
array.push({
"user_id" : data,
"device":settings.device, // output: settings.device is undefined
"token": example.token
});
});
});
// Send array to client
This is really a question of how to handle multiple resolved values in a Promise chain. You can search for that and see lots of great examples on how to handle it. For example, you could return an array or object in each then handler, or re-assign values to higher-scoped variables (as you're doing with settings). I've used both methods heavily in the past, and the resulting code is obviously inelegant and not fun to write.
However, async/await is readily available in Node and simplifies your code quite a bit:
const array = [];
// Get settings.
const settings = await Settings.findOne({where: {
user_id: data
}});
// Get token.
const example = await Example.findOne({where: {
user_id: data
}});
array.push({
user_id : data,
device: settings.device,
token: example.token
});
Sequelize return the model object you can get value by dataValue
console.log(settings.dataValues.device);
or if you want lean data
Settings.findOne({where: {
user_id: data,
raw:true,
}})
.then(settings => {
device = settings.device;
console.log(device);
});

array schema single value mongodb using node js

I am trying to fetch single value from my level collection using this level schema
After new Schema I use ( [] ) I don't know exactly the use of this. Why we use this. But my project requirement is to use this []
var LevelSchema = new Schema(
[
{
_id: { type: String },
age: { type: Number },
level_num: { type: Number },
min_score: { type: Number },
max_questions: { type: Number }
}
]);
So when I am trying to fetch the value using this node js
this.levelChange = function(req, res, next){
try{
var query = {level_num:1};
QuizLevels.find(query,function(err,data){
if(err){
return next(err);
}else if(data.min_score===10){
console.log(data.min_score);
}else{
console.log("Error");
}
});
}catch(err){
console.log(err);
}
};
I proper send all level information stored in my MongoDB to the browser. But when I am trying to fetch a single value field
min_score from the database and trying to send it in the browser or in the console it shows undefined. Means value cannot parse properly from MongoDB
to browser or console using this node js code.
so there are any further steps before to fetch this array schema.So that single value field parses properly to console
3). This is my JSON data
I am not sure that json data is exact according to level schema that i used to fetch.
{
"age":5,
"level_num":1,
"min_score":10,
"max_questions":30
}
{
"age":5,
"level_num":2,
"min_score":12,
"max_questions":33
}

js-data v3- Keeping meta information in a paginated end point

I'm trying to deserialize a paginated end point. The return request for this end point looks like
{
count: number,
next: string,
previous: string,
data: Array[Objects]
}
The issue I'm having when using js-data to do a findAll, it's injecting this object into the data store. It should be injecting the objects in the data array into the store. So I made a deserialize method on my adapter that looks like this.
deserialize: (resourceConfig:any, response:any) => {
let data = response.data;
if (data && 'count' in data && 'next' in data && 'results' in data) {
data = data.results;
data._meta = {
count: response.data.count,
next: response.data.next,
previous: response.data.previous
};
}
return data;
}
And this works. The array objects are getting injected into my data store. But the meta information is getting lost.
dataStore.findAll('User').then(r => console.log(r._meta)); // r._meta == undefined
I would like to keep that meta information on the returned object. Any ideas?
To do this in v3 you just need to override a couple methods to tweak JSData's
handling of the response from your paginated endpoint. The two most important
things are to tell JSData which nested property of the response are the records
and which nested property should be added to the in-memory store (should be the
same nested property in both cases).
Example:
const store = new DataStore({
addToCache: function (name, data, opts) {
if (name === 'post' && opts.op === 'afterFindAll') {
// Make sure the paginated post records get added to the store (and
// not the whole page object).
return DataStore.prototype.addToCache.call(this, name, data.results, opts);
}
// Otherwise do default behavior
return DataStore.prototype.addToCache.call(this, name, data, opts);
}
});
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('post', {
// GET /posts doesn't return data as JSData expects, so we've got to tell
// JSData where the records are in the response.
wrap: function (data, opts) {
// Override behavior of wrap in this instance
if (opts.op === 'afterFindAll') {
// In this example, the Post records are nested under a "results"
// property of the response data. This is a paginated endpoint, so the
// response data might also have properties like "page", "count",
// "hasMore", etc.
data.results = store.getMapper('post').createRecord(data.results);
return data
}
// Otherwise do default behavior
return Mapper.prototype.wrap.call(this, data, opts);
}
});
// Example query, depends on what your backend expects
const query = { status: 'published', page: 1 };
posts.findAll(query)
.then((response) => {
console.log(response.results); // [{...}, {...}, ...]
console.log(response.page); // 1
console.log(response.count); // 10
console.log(response.hasMore); // true
});

Categories

Resources