I'm trying to interact with the API of processmaker.
I have made a simple form to authenticate and get the authorization token, which is needed to interact with the rest of the API.
I am able to use the token to output a json response of created projects after login. The response is an array of objects.
I need to get the prj_uid for an api request so I want to extract these, but I've had little luck using map.
How can I iterate over the response and get prj_name and prj_uid for each of the objects in the array?
import React, { useState, useEffect } from "react";
//import ResponsiveEmbed from "react-responsive-embed";
const Tasks = ({ loggedIn }) => {
const [hasError, setErrors] = useState(false);
const [projects, setProjects] = useState([]);
const url = "http://127.0.0.1:8080/api/1.0/workflow/project";
useEffect(() => {
let access_token = sessionStorage.getItem("access_token");
async function fetchData() {
const response = await fetch(url, {
method: "GET",
withCredentials: true,
timeout: 1000,
headers: {
Authorization: "Bearer " + access_token
}
});
response
.json()
.then(response => setProjects(response))
.catch(err => setErrors(err));
}
fetchData();
}, [loggedIn]);
console.log(JSON.stringify(loggedIn) + " logged in, displaying projects");
console.log(projects + " projects");
if (!loggedIn) {
return <h1>Error</h1>;
} else {
return (
<>
<p>Login success!</p>
<h2>Projects:</h2>
<span>{JSON.stringify(projects)}</span>
<div>Has error: {JSON.stringify(hasError)}</div>
</>
);
}
};
export default Tasks;
Stringified Response:
[
{
"prj_uid":"1755373775d5279d1a10f40013775485",
"prj_name":"BPMN Process",
"prj_description":"This is a processmaker BPMN Project",
"prj_category":"8084532045d5161470c0de9018488984",
"prj_type":"bpmn",
"prj_create_date":"2019-08-13 08:50:25",
"prj_update_date":"2019-08-13 09:04:16",
"prj_status":"ACTIVE"
},
{
"prj_uid":"7459038845d529f685d84d5067570882",
"prj_name":"Purchase Request",
"prj_description":"",
"prj_category":"2284311685392d2e70f52e7010691725",
"prj_type":"bpmn",
"prj_create_date":"2019-08-13 11:30:48",
"prj_update_date":"2019-08-13 12:20:05",
"prj_status":"ACTIVE"
}
]
Array.map() is your answer- you had it right.
its as simple as:
let mappedObject = result.map( el => ({ prj_name, prj_uid }) );
el is every element in the array, and we construct the new array with an object containing only prj_name and prj_uid. Because el alraeady has those properties with those names, we do not need to write { prj_name: el.prj_name } when we construct the new object, it is implied and will do the trick with only the property names there.
mappedObject will now hold an array of objects consists only of the asked properties.
You might wanna read more about map to understand it better- Array.map()
If loggedIn is the json object, then you can do this:
const uidNameArr = loggedIn.map((item) => { // returns an array of arrays with the values you want.
return [item.prj_uid, item.prj_name]
})
uidNameArr.forEach(([uid,name]) => {
console.log(`${name} has a uid of ${uid}`)
})
Related
I am trying to pass two parameters in createProductReview action when submitHandler function runs.
One is id and other one is an object containing two items. The problem is that I am unable to pass the object in createProductReview action. It gives undefine when I console log it in reducer function. I want to know how can I pass these two arguments without getting error.
Please check out attached image for error
submitHandler function
const submitHandler = (e) => {
e.preventDefault();
dispatch(createProductReview({ id, { rating, comment } }));
};
createProductReview
export const createProductReview = createAsyncThunk(
'reviewProduct',
async ({ productId, review }, thunkAPI) => {
console.log(productId, review);
try {
const {
userLogin: { userInfo },
} = thunkAPI.getState();
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
};
await axios.post(`/api/products/${productId}/reviews`, review, config);
} catch (error) {
const newError =
error.response && error.response.data.message
? error.response.data.message
: error.message;
return thunkAPI.rejectWithValue(newError);
}
}
);
In javascript, you need to pass keys to the object so it should be like this
dispatch(createProductReview({ productId:id, review:{ rating, comment } }));
Specially, when you are destructuring it in the function. Since destructure works by getting the object with its key.
so for example:
const x = {temp:"1"}
const {temp} = x;
console.log(temp);
//1
I am trying to use a Svelte datastore and a Restapi backend to store, display and update a simple list of objects.
The front end has a table of items, a text entry, and a button to add to the list.
When I add to the list the datastore seems to update okay, as does the backend JSON.
The table in the frontend however only display "undefined" until the page is refreshed manually in the browser.
The logic I have tried to use is as follows:
The App function handleAddItem() triggers the function addData() on the custom store and then it
does an update on the items = [...items, new_data].
Table shows undefined until the page is refreshed
Any clues what I might be doing wrong?
api.js backend server
export async function fetchData(){
console.log("fetching data");
const response = await fetch("http://localhost:3000/data")
const data = await response.json();
return data
}
export async function addItem(item_label){
console.log("Backend api is posting item: ", item_label);
const newlyAddedItem = {item_label};
const settings = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(newlyAddedItem)
};
try {
const url = `http://localhost:3000/data`;
const response = await fetch(url, settings);
const newlyAddedItem = await response.json();
console.log("returned data after POST", newlyAddedItem)
} catch (e) {
return e;
}
}
datastore.ts
import { writable } from "svelte/store";
import { fetchData, addItem } from "../backend/api.js";
import type item from "../../src/App.svelte";
function alistStore() {
const { subscribe, update, set } = writable([])
return {
subscribe,
update,
loadData: async () => {
let data = await fetchData();
set(data);
return data
},
addData: async (item_label) => {
console.log("Datastore handling add item: ", item_label)
let new_data: item = await addItem(item_label);
return new_data
}
};
}
export let alist_store = alistStore();
App.svelte
<script lang="ts">
import { alist_store } from "./stores/datastore";
import { onMount } from "svelte";
interface item {
"id": number,
"item_label": string
};
export let items: item[];
export let new_data;
export let item_label = "";
alist_store.subscribe((data) => items = data)
onMount(() => {
alist_store.loadData();
})
async function handleAddItem(item_label) {
console.log("App is handling add item: ", item_label);
new_data = alist_store.addData(item_label);
console.log("updating items")
items = [...items, new_data];
console.log("updated items")
};
</script>
<main>
<table>
<th>Id</th>
<th>Name</th>
{#each items as item (item.id)}
<tr>
<td>{item.id}</td>
<td>{item.item_label}</td>
</tr>
{/each}
</table>
<div>
<textarea bind:value={item_label}></textarea>
<button class="button" on:click={() => handleAddItem(item_label)}></button>
</div>
</main>
I have tried a few things:
1: reloading the data from the store
2: trying to add reactive syntax to the => $items
3: I tried defining the object type as "item" before adding it to the list.
I think it might be to do with how things are being handled by the async and await, but this is all new to me - so I am left a bit confused.....
It looks like a problem with the order in things are happening??
There's the log console.log("returned data after POST", newlyAddedItem) at the end of addItem inside api.js but newlyAddedItem isn't returned
alist_store.addData is async -> new_data = await alist_store.addData(item_label);
(if the variable new_data is only needed inside this function it wouldn't have to be declared on top level)
Change the function on the addAlbum in the datastore.
It needed to handle the logic for the append to the current array of data, this means it is guaranteed to always return an array. No promises broken and the components all refresh instantly after the add album is executed.
Debugging the code to print to the console showed that when I expected it should be an array, it was an album.
addAlbum: async () => {
let data = await fetchData();
let album = await addAlbum({
"id": "99",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 99.99
});
data = [...data, album]
set(data);
}
I make a request to 'https://pokeapi.co/api/v2/pokemon/${name}' to obtain the information of a specific pokemon, the problem is that the name must be the same otherwise it returns undefined, I need to filter many pokemons, for example: if I search for char it should return charmeleon and charizard, because they both have 'char'. How can I filter a lot of pokemons?
const params = {
headers: {
'Content-Type': 'application/json'
}
}
const searchPokemon = async name => {
const url = `https://pokeapi.co/api/v2/pokemon/${name}`
try {
const response = await fetch(url, params);
const result = await response.json();
return result;
} catch (error) {
console.log(error)
}
}
To get a list of all possible pokemon names, you can make a request to https://pokeapi.co/api/v2/pokemon?limit=100000 (where 100000 is larger than the number of pokemon that exist. There appear to be only 1118 pokemon as of now.)
The result looks like this:
[
{
name:"bulbasaur",
url:"https://pokeapi.co/api/v2/pokemon/1/"
},
{
name:"ivysaur",
url:"https://pokeapi.co/api/v2/pokemon/2/"
},
{
name:"venusaur",
url:"https://pokeapi.co/api/v2/pokemon/3/"
},
...
]
Then you can filter out that list based on the names you are looking for. After you find the name you want, you can use the corresponding URL to get more information.
I'm new to Next Js and functional comoponents. I'm trying to retrieve data from /api/retrieve2
//this is retrieve page
export default function Retrieve() {
const onSubmit = async data => {
const { user } = await axios.post("/api/retrieve2", data);
console.log(user) // user here is undefined
};
return (...);
}
//this is retrieve2, inside the API folder
export default async (req, res) => {
try {
const { data } = await axios.post(myBackendUrl, req.body);
console.log(data) //this is printing the right data - { email: 'casas#gmail.com', code: '123123' }
res.json(data);
} catch (e) {
res.json({ err: e.message || e });
}
};
What am I missing, is this something about Next? About functional components?
You should read about ES6 destructuring
You try to destructure user but the axios respons witch is a object doesnt contain the key user
For data it works because there is a data property in the response
Here are all properties that you can destructure:
{ data, status, statusText, headers, config, request }
You need to get the full URL to make http request to using getInitialProps, here Home is the name of your component
const Home = ({ENDPOINT}) => {
const onSubmit = async data => {
const { data } = await axios.post(`${ENDPOINT}/api/retrieve2`, data);
// consider changing `user` here to `data` since Axios stores response in data object
console.log(data) // should be defined
};
return (...);
}
Home.getInitialProps = ctx => {
const ENDPOINT = getEndpoint(ctx.req);
return { ENDPOINT };
};
// You should store this somewhere you can reuse it
export function getEndpoint(req) {
return !!req
? `${req.headers['x-forwarded-proto']}://${req.headers['x-forwarded-host']}`
: window.location.origin;
}
I'm starting to study redux now. I used the real-word example as a starting point, using normalizr and reselect to handle data.
Now, I need to understand where is the best place to convert dates coming from the server into js Date objects. Since normalizr already takes care of "some Schemas" I thought it could do that too, but I did not find it there.
Where should I convert these dates? My assumption is that I have to keep those dates already converted on the store. Is that right?
Normalizr can do this - in the current version (v3.3.0) you can achieve it like this:
import { schema } from 'normalizr';
const ReleaseSchema = new schema.Entity('releases', {}, {
processStrategy: (obj, parent, key) => {
return {
...obj,
createdAt: new Date(obj.createdAt),
updatedAt: new Date(obj.updatedAt),
};
},
});
I added a third parameter to callApi (inside api middleware):
function callApi(endpoint, schema, conversionFromServer) {
const fullUrl = (endpoint.indexOf(API_ROOT) === -1) ? API_ROOT + endpoint : endpoint
return fetch(fullUrl)
.then(response =>
response.json().then(json => ({ json, response }))
).then(({ json, response }) => {
if (!response.ok) {
return Promise.reject(json)
}
const camelizedJson = camelizeKeys(json)
const nextPageUrl = getNextPageUrl(response)
let convJson = camelizedJson;
if(conversionFromServer) {
convJson = conversionFromServer(convJson);
}
return Object.assign({},
normalize(convJson, schema),
{ nextPageUrl }
)
})
}
and now I can call it like this:
return {
[CALL_API]: {
types: [ TIMESLOTS_REQUEST, TIMESLOTS_SUCCESS, TIMESLOTS_FAILURE ],
endpoint: `conference/${conferenceId}/timeslots`,
schema: Schemas.TIMESLOT_ARRAY,
conversionFromServer: (data) => {
data.timeslots.forEach((t)=>{
t.startTime=php2js.date(t.startTime)
t.endTime=php2js.date(t.endTime)
});
return data;
}
}
}
This way I keep this conversion "as close to the server" as I can.