Cypress - Condition depending on json list - arrays

I have a json list containing some several hundreds of entries. I want to check whether the list contains a specific entry.
I tried using .contains as a chainer, but this is not accepted by Cypress:
const permission_list = require('../../fixtures/permissions/permissions_admin.json')
describe('Test plan creator for Administrator', () => {
it('create a test plan', () => {
cy.get(permission_list[0].granted_permissions).then((list) => {
if (list.contains('dyna'))
{cy.log('Yes')}
else
{cy.log('No')}
})
})
})
This leads to the error message:
TypeError
list.contains is not a function
{edit} I also tried .includes instead of .contains with the same result
Just for information, this is what the json list looks like (the list I am interested is the list "granted permissions")
[
{
"name": "administrator",
"title": "Administrator",
"description": "",
"is_external": "0",
"granted_permissions": [
"dyna",
"jobcards",
...
...
],
"denied_permissions": [],
"inherited_roles": []
}
]

You can use the Jquery inArray method.
describe('Test plan creator for Administrator', () => {
it('create a test plan', () => {
cy.get(permission_list[0].granted_permissions).then((list) => {
if (Cypress.$.inArray('dyna', list) != -1) {
cy.log('Yes') //dyna found
} else {
cy.log('No')
}
})
})
})

Related

array of objects map is not a function

I'm new to React, I'm pulling an API, which is filled with an array of objects.
When trying to map it I get following error: "Uncaught TypeError: benchmarkData.map is not a function"
While I get the error, it actually does the job (when I log it I get the asked result), it just prevents loading the other elements.
This is how my API looks:
[
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "412",
"DEGEMAT": "235",
"DEGEMGW": "97.7",
"DEGEMMB": "3.39",
"DEGEMNV": "64.1",
"DEGEMSD": "13.2",
"DEGEMVD": "71.3",
"DEJAART": "2022",
"DELTMCE": "-",
"DEWEENR": "1"
},
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "436",
"DEGEMAT": "233",
"DEGEMGW": "120.1",
"DEGEMMB": "3.38",
"DEGEMNV": "64.2",
"DEGEMSD": "13.2",
"DEGEMVD": "71.4",
"DEJAART": "2022",
"DELTMCE": "ALL",
"DEWEENR": "1"
},
{
"type": "drgmbmk",
"test": "5782",
"DEAANTA": "22",
"DEGEMAT": "213",
"DEGEMGW": "99.9",
"DEGEMMB": "3.23",
"DEGEMNV": "64.9",
"DEGEMSD": "12.7",
"DEGEMVD": "72.8",
"DEJAART": "2022",
"DELTMCE": "BBC",
"DEWEENR": "1"
}
]
This is my React code:
useEffect(() => {
if (benchmarkData) {
setSupplierHasData(true);
let loadingplaceFilter = [];
const loadingPlaces = [];
benchmarkData.map(item => { //!start of requested question code
if(item.type === "drlvbmk") {
loadingPlaces.push(item.DELPLCE, item.DELTMCE, item.DEELTCE)
}
}
); //!end of requested question code
console.log(loadingPlaces);
const uniqueLoadingplaces = [];
loadingPlaces.map((item) => {
var findItem = uniqueLoadingplaces.find((x) => x === item);
if (!findItem) uniqueLoadingplaces.push(item);
});
console.log('uniqueloadingplaces', uniqueLoadingplaces);
setFilters({
...filters,
loadingplaces: loadingplaceFilter,
});
setFilterData(true);
}, [benchmarkData]);
Pulling the api:
useEffect(() => {
const fetchBenchmarkData = async () => {
setFetchNewData(false);
console.log('current user active id :', suppliersState.suppliers.activeID, "current user active uuid: ", suppliersState.suppliers.activeUUID);
try {
await axios
.get(
`https://bpg-ap.ddev.site/jsonapi/benchmarking_get_data?supplierid=${suppliersState.suppliers.activeID}&date_from=1&date_to=2022`
)
.then(res => {
setBenchmarkData(res.data);
console.log('BENCHMARKINGRESULTAAT : ', res.data);
});
} catch(error) {
setSupplierHasData(false);
setChartData({});
console.log(error);
}
};
if (benchmarkData === null) {
console.log('useEffect fired, fetch benchmarkData', benchmarkData);
fetchBenchmarkData();
}
}, [benchmarkData, suppliersState.suppliers.activeID]);
Logging benchmarkData results in:
I hope anyone can help me to solve this problem

Can't use the data from API when app just starts

My data is undefined when the app is started but after the refresh, the data comes perfectly.
For startup
It gives me [Unhandled promise rejection: TypeError: Object.entries requires that input parameter not be null or undefined]
But after the refresh, the data comes perfectly and everything working.
This is part of my data
Object {
"attributes": Object {
"htmlName": null,
"id": 0,
"items": Array [
Object {
"htmlName": "r_1",
"name": "m2 (Brüt)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_2",
"name": "m2 (Net)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_164",
"name": "Arsa Alanı (m2)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "a_137",
"name": "Oda Sayısı",
"numeric": false,
"options": Object {
"12": "1+0",
"13": "1+1",
"14": "1.5+1",
"15": "2+0",
"16": "2+1",
"17": "2.5+1",
"18": "2+2",
"19": "3+1",
"20": "3.5+1",
"21": "3+2",
"22": "4+1",
"226": "0+1",
"23": "4.5+1",
"24": "4+2",
"25": "4+3",
"26": "4+4",
"27": "5+1",
"28": "5+2",
"29": "5+3",
"30": "5+4",
"31": "6+1",
"32": "6+2",
"33": "6+3",
"34": "7+1",
"35": "7+2",
"36": "7+3",
"37": "8+1",
"38": "8+2",
"39": "8+3",
"40": "8+4",
"41": "9+1",
"42": "9+2",
"43": "9+3",
"44": "9+4",
"45": "9+5",
"46": "9+6",
"47": "10+1",
"48": "10+2",
"49": "10 Üzeri",
},
"order": 0,
"required": true,
},
api.js
export const getData = function () {
return axios
.get(
"blabla",
{
headers: {
Authorization: `blabla`,
},
}
)
.then((json) => {
if (json && json.status === 200) {
//console.log(json);
return json.data;
}
})
.catch((e) => {
console.log(e);
});
};
App.js
const [data, setData] = useState({});
const [roomValue, setRoomValue] = useState(null);
const [roomCount, setRoomCount] = useState([]);
const [isFocus, setIsFocus] = useState(false);
useEffect(() => {
getDataFunc();
//setDropdown(data.attributes.items[3].options);
}, []);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};
function setDropdown(query) {
const response = query;
try {
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
//console.log(roomCount);
} catch (error) {
//console.log(error);
}
}
How can I fix that ?
Add a seperate useEffect to check wheather the data has been set and then only set the dropdown values
useEffect(() => {
getDataFunc();
}, []);
useEffect(() => {
if(data && data.attributes?.items[3]){
setDropdown(data.attributes.items[3].options);
}
}, [data]);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};
It seems like the error is caused by the attributes property being empty when you try to access it. But when you assign them one by one then it loads because the data is loaded per nested property before assigning it to the variable. Means it hasn't fully loaded yet
const response = data.attributes.items[3].options;
It outputs an error because attributes is undefined. So it's not an object, therefore, attributes.items is considered invalid
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes.items[3].options
console.log(specificData) //
So one solution would be using the optional chaining operator to avoid the error, it's just basically a question mark (?) after the object you are trying to access.
The response would be then 'undefined'. That way even if the attributes is empty or not, data will be assigned to the response constant then you can just add some more checking outside of that.
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes?.items[3].options
console.log(specificData) // outputs undefined instead of an error
Let me know if this works btw. maybe you could provide the actual api or maybe a sample api endpoint so we could test it directly. Or maybe the full code?
I've encoutered this before though I'm not 100% sure this is all I've done. But for the error I'm sure the optional chaining operator will prevent it
Try calling getData inside an async function and wait for the process to complete like this in your App.js
const [data, setData] = useState([]);
const [roomCount, setRoomCount] = useState([]);
useEffect(() => {
getDataFunc()
}, []);
const getDataFunc = async() => {
await getData(setData);
const response = data;
console.log(response);
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
console.log(roomCount);
}
note: The best practice is not to directly pass the setData function to getData api call instead return the response from api and assign the response in main code like below
const response = await getData();
setData(response)
From what I see, your data.attributes has undefined value.
Please double-check everything, it is technically impossible to get data directly if data.attributes is undefined

ReactJS: axios POST with recursive function

CreateFolder.js
Hi!! I'm trying loop an js tree object to create a folder for the childs elements with de Egnyte API.
I think I have posed the problem wrong.
My recursive funcion is cheking if the parent have childs, if this true, loop his childs, if the child isn't a parent, i want to create the folder with this child id.
The problem is when I see the console, first there are all prints of the '-----ADD-----', after that, there are all prints of the '-----ADDED' so I don't understant why they aren't simultaneously
The API works fine, this create my 6 folders, the problem of this code is when I loop my original TREE that have 400 childs, and I need to create this 400 folders but the callbacks doesn't work, this creates about 60 folders, and there are much 403 errors. I think there are much calls in a short time.
I tried to call the function addFolder with a time out or doing the function async but doesn't work too.
I explained my problem well? Can someone help me?
Thank you so much!!
import { ConnectingAirportsOutlined } from "#mui/icons-material"
import axios from "axios"
import { useEffect, useState } from "react"
import { useSelector } from "react-redux"
export default function CreateFolders() {
console.log('======CreateFolders=====')
// const state = useSelector(state => state)
// const tree = state.assets.tree
const tree = [
{
id: '1000',
name: 'GRUPO GENERADORES',
child: [
{
id: '1100',
name: 'MOTORES',
child: [
{
id: '1100.1',
name: 'MOTOR 1'
},
{
id: '1100.2',
name: 'MOTOR 2'
},
{
id: '1100.3',
name: 'MOTOR 3'
},
]
},
{
id: '1200',
name: 'ALTERNADORES',
child: [
{
id: '1200.1',
name: 'ALTERNADOR 1'
},
{
id: '1200.2',
name: 'ALTERNADOR 2'
},
{
id: '1200.3',
name: 'ALTERNADOR 3'
}
]
}
]
}
]
useEffect(() => {
getTreeItems(tree)
}, [tree])
const api = 'https://test.egnyte.com/pubapi/v1/fs/Shared/test/'
const headers = {
"headers": {
"Authorization": "Bearer XXXXXXXXXX",
"Content_type": "application/json"
}
}
const body = {
"action": "add_folder"
}
const addFolder = async (id) => {
const endpoint = api + id
await axios.post(endpoint, body, headers).then(res => {
console.log('OK')
return
}).catch((error) => {
console.log('ERROR')
})
}
const getTreeItems = treeItems => {
return treeItems.map(item => {
if (item.child && item.child.length > 0) {
getTreeItems(item.child)
}
if (item.id.includes('.')) {
console.log('-------ADD-------')
console.log(item.id)
addFolder(item.id)
console.log('-------ADDED')
}
})
}
return (
<>
<h2>CreateFolders</h2>
</>
)
}
The description you provided seems like you reached the rate limit (per second/daily).
Try to explore the response headers in failed requests to get more information about:
What kind of quotas have you exceeded (per second/daily)
When you can retry the request (e.g Retry-After header)
There are 2 solutions you can try:
Use bulk operation (create all the entities by single request) if the API supports it. This is a preferable solution
Retry the request after the timeout you get from the failed request header (e.g Retry-After header)
I solved my problem with the setTimeout with an index, because my function was executing the whole thing after 3 seconds, and with this index, add more time to every item, and executes each item 3 seconds after the previous one
My function after:
folders.map((id) => {
setTimeout(() => {
addFolder(id)
}, 3000)
})
My function before:
folders.map((id, index) => {
setTimeout(() => {
addFolder(id)
}, 3000 * index)
})
It's silly but I didn't know how the setTimeout worked

How to chain actions in ReactJS?

How to chain actions to get the url of image property when I fetch the post list.
I've made a request that fetch all posts, and it gives me a link for the property "image".
mywebsite/api/recipes?_page=1 :
{
"#context": "/api/contexts/Recipes",
"#id": "/api/recipes",
"#type": "hydra:Collection",
"hydra:member": [
{
"#id": "/api/recipes/524",
"#type": "Recipes",
"id": 524,
"category": "/api/categories/11",
"title": "NewPost",
"content": "This is a new post",
"time": "50 minutes",
"image": [
"/api/images/37"
],
"slug": "new-post",
"createdAt": "2020-06-30T10:26:00+00:00",
"comments": [
"/api/comments/1359",
"/api/comments/1360"
]
},
........
and the result for mywebsite/api/images/37 is :
{
"url": "/images/5efbe9a4a1404818118677.jpg"
}
now in my actions i have
export const recipesListError = (error) => ({
type: RECIPES_LIST_ERROR,
error
});
export const recipesListReceived = (data) => ({
type: RECIPES_LIST_RECEIVED,
data
});
export const recipesListFetch = (page = 1) => {
return (dispatch) => {
dispatch(recipesListRequest());
return requests.get(`/recipes?_page=${page}`)
.then(response => dispatch(recipesListReceived(response)))
.catch(error => dispatch(recipesListError(error)));
}
};
so the first request is recipesListFetch, now what is missing is the second request to get the image and then return the url so i can directly have access to the image for each post
the easy solution would have been to use normalization_context groups has
i'm working with symfony api platform but it still gives me a link for the image property, I think because it's a ManyToMany relation
There don't seem to have the need for normalisation. The images and comments are specific to the recipe.
Make the then block callback as async fun and inside then block loop thru the recipes array first and then loop thru the image array and make api call for the image and await for it.
export const recipesListFetch = (page = 1) => {
return (dispatch) => {
dispatch(recipesListRequest());
return requests
.get(`/recipes?_page=${page}`)
.then(async (response) => {
//make then callback as async fun
const recipes = response["hydra:member"];
const imagesForTheRecipie = [];
for (let i = 0; i < recipes.length; i++) {//loop thru recipies
for (let j = 0; j < recipes[i].image.length; j++) {//loop thru images for each recipie
const imageUrl = recipes[i].image[j];//grab the image url
const image = await requests.get(`/${imageUrl}}`);
imagesForTheRecipie.push(image);
}
recipes[i].image = imagesForTheRecipie; //mutate the object which will directly update the response
}
dispatch(recipesListReceived(response));
})
.catch((error) => dispatch(recipesListError(error)));
};
};
Note - If you want to normalise then you can choose to nomalise data for the categories as the same category will be used by many recipes. In that case you will have to re-structure your reducers.

Update Object Inside the Array of object in a mongoose document with projection or select

I am trying to update the object inside the document
Document: Cats
{
"_id": "5e5cb512e90bd40017385305",
"type": "cat"
"history": [
{
"id": "randomID",
"content": "xyz",
},
{
"id": "randomID2",
"content": "abc",
}
]
}
Code to select and update the object inside the history array:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true,
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Input has following values
input: {
catId: "5e5cb512e90bd40017385305",
historyId: "randomID",
history: {
id: "randomID",
content: "new content"
}}
I tried using Projection, I used select changed it to field, found in mongoose documentation.
I still couldn't update the values. Is there anything wrong with the way i am querying or selecting the subfield.
Found the Solution for it by going through more detail of the operator($set) and option(new, fields).
Question:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
// using new option would return the new document
new: true,
/* using fields option would select the based on the given key, but using new:
true with fields will throw error: 'cannot use a positional projection and
return the new document'
*/
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
This post below answers that question for *error: 'cannot use a positional projection and return the new document'.
https://stackoverflow.com/a/46064082/5492398
Final Solution:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Removing field option, since I don't need the unmodified selection before atomic modification, solves the question.

Resources