Pass Parameters Besides URL in Next JS in Client Side API Fetch - rest

I've been working on calling an internal GetCompanies API through the website client side.
So far I made attempts like this based on search results and documentation
// const fetcher = (includeFiles, folderLocation, SharePointCreds) => fetch(includeFiles, folderLocation, SharePointCreds).then(res => res.json());
// const { data, error } = useSWR('http://localhost:55419/api/SharePoint/GetCompanies', fetcher);
// if (error) return <div>failed to load</div>
// if (!data) return <div>loading...</div>
// render data
//return <div>hello {data}!</div>
useEffect((includeFiles, folderLocation, SharePointCreds) => {
fetch('http://localhost:55419/api/SharePoint/GetCompanies', includeFiles, folderLocation, { method: "POST", body: SharePointCreds})
.then((res) => res.json())
.then((data) => {
console.log("data is ", data)
})
}, [])
if (isLoading) return <p>Loading...</p>
// async function GetCompanies(includeFiles, folderLocation, SharePointCreds){
// const res = await fetch('http://localhost:55419/api/SharePoint/GetCompanies');
// const companies = await res.json();
// }
// return (
// <div>
// <p>{data}</p>
// </div>
// )
corresponding to an API call which works when we test it in Swagger
And I got generic error messages like TypeError: Failed to fetch
I guess the error is in how I pass in includeFiles and folderLocation since I haven't yet found documentation or search results for how to pass in parameters besides the URL, though I could be wrong.
I define all the arguments earlier in the portfolio component for now.
Do you see how else I should pass in includeFiles and folderLocation?
Also, please be kind because I'm learning and doing my best and I've worked on this for a few hours already.

Related

Suspense returns data too fast in #tanstack/react-query v4

I am upgrading a React app from react-query v3 to #tanstack/react-query v4.
Almost everything works, but I'm having a problem with Suspense.
I have a react component:
const WrapperPageEdit: React.FC<MyProps> = ({
pageUuid,
redirect,
}: MyProps) => {
const FormPage = React.lazy(() => import('./FormPage'));
const { data } = usePageView(pageUuid);
if (data?.[0]) {
const pageObjectToEdit= data[0];
const content = pageObjectToEdit.myStuff.content;
return (
<Suspense
fallback={<Trans id="loading.editor">Loading the editor...</Trans>}
>
<FormPage
id={uuid}
content={content}
redirect={redirect}
/>
</Suspense>
);
}
return <p>No data.</p>;
};
And here's my query:
export function usePageView(
uuid: string,
): UseQueryResult<DrupalPage[], Error> {
return useQuery<DrupalPage[], Error>(
queryKeyUsePageView(uuid),
async () => {
return fetchAnon(getPageByPageUuid(uuid));
},
{
cacheTime: YEAR_MILLISECONDS,
staleTime: YEAR_MILLISECONDS,
onSuccess: (data) => {
if (data?.[0]) {
data.map((element) => processResult(element));
}
},
},
);
}
This works in v3 but fails in v4 with the following error:
TypeError: Cannot read properties of undefined (reading 'content')
The reason the property is undefined is because that property is set by the processing in onSuccess (data.map).
The issue appears to be that in v4, the component WrapperPageEdit is refreshed before onSuccess in the usePageView query has finished processing, whereas in v3, the component WrapperPageEdit is not refreshed until the onSuccess data.map is complete.
How can I correctly fix this? I can write some additional code to try to check whether the onSuccess data.map is complete, but since react-query handled this automatically in v3, I'd like to rewrite my code in v4 so that it is the same.
The problem is likely that you are mutating the data in onSuccess. Directly modifying data in callbacks is not a good idea. Instead, do your transformation for example directly in the queryFn:
async () => {
const data = fetchAnon(getPageByPageUuid(uuid));
if (data?.[0]) {
data.map((element) => processResult(element));
}
return data
},
other good places to do data transformation is e.g. the select option, but it should always happen in an immutable way, because otherwise, you are overwriting the cached data inadvertently. React prefers updates to be immutable.

Populate a database with datas from a graphql api

I'm searching, as the topic name says, to populate a database with datas coming from an already exist graphql API.
My project is using Next.js, PostgreSQL, Apollo for GraphQL and Prisma to connect the whole. I managed to retrieve datas from the API and display these. Now I just want to store these datas in my Postgres database. So you know I'm using the Client-Side Rendering and not the other methods (SSR, SSG).
All documentations I found was about creating an API then post to the database which is not my case or documentation about create & createMany queries but with hardcoded datas. My research doesn't help me to see clear so I come here to find anyone who is willing to guide me. If have a clue I'll appreciate. Thank you =) !
To illustrate here a piece of code :
import { useQuery, gql } from "#apollo/client";
// query to retrieve 3 animes
const QUERY = gql`
query GetFirstsThree{
Page(page: 1, perPage: 3) {
media {
title {
userPreferred
}
}
}
}
`;
export default function AnimList() {
const { data, loading, error } = useQuery(QUERY);
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
return null;
}
const medias = data.Page.media;
return (
<div className="mainGrid">
{medias.map((value) => {
return (
<p>{value.title.userPreferred} </p>
)
})}
</div>
);
}
Hello for those who have the same issue here is how i resolved my problem.
First I have created a async function to post my Anime.
async function savedAnime(anime){
const res = await fetch('api/anime', {
method: 'Post',
body: JSON.stringify(anime),
});
if(!res.ok){
throw new Error('Something went wrong');
}
return await res.json();
}
Then I added a submit input, which on its onClick event will map the medias the same way I did to display the anime's title, but here to store the datas in the database.
After, I create a variable with my datas that I want to store. Finally, I call the function I've created before to post my Anime, on which I pass the variable.
And that pretty much all.
<input type="submit" value="Save Animes" onClick={ async () => {
try{
medias.map(async (value) => {
const anime = {
title: value.title.userPreferred,
coverImage: value.coverImage.medium,
};
console.log(anime);
savedAnime(anime);
})
} catch(err){
console.error(err);
}
}}/>

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

Ionic view not updating after return from provider promise

I'm very new to Ionic and JS programming in general so please forgive my ignorance. I've been able to get data from other REST providers I've setup and have the updated values display fine. Pretty much copied the code from some other working functions. This time, no matter what I try, nothing will update.
Provider:
return new Promise(resolve => {
this.http.post(this.apiUrl)
.subscribe(res => {
resolve(res);
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
this.error = {"text":"App error occured."};
console.log('Client-side error occured.');
} else {
this.error = {"text":"Cloud server error occured."};
console.log('Cloud server error occured:'+err);
}
return this.error;
});
});
}
HTML:
<ion-item>
<ion-label stacked>Make</ion-label>
{{vesselData?.make}}
</ion-item>
Function:
vesselData = {"make":""};
updateVesselInfo() {
const data = JSON.parse(localStorage.getItem('userData'));
this.vesselProvider.getVesselData(data.userData.sim).then(vData => {
this.vesselData = vData;
}).catch(console.log.bind(console));
}, (err) => {
console.log("Vessel: ".err);
});
If I log the data returned from the provider in the .then(), it shows the provider returned the correct data. However, it's not updating any of the vesselData variables. Any idea where I'm going wrong here?
So modern way is to provide method in your provider that returns Observable and then in your component you just call this method and subscribe to it to obtain data:
In your provider:
getVesselData() {
return this.http.post(this.apiUrl)
.pipe(
catchError(this.yourErrorHandlerInsideProviderHere)
)
}
Now in your component:
vesselData = {"make":""};
updateVesselInfo() {
this.provider.getVesselData().subscribe( vesselData => {
this.vesselData = vesselData;
})
}
So ideal is to keep error handling inside provider here and within component your methods should be light weight.
This example should work for you as long as you are on Angular 4.3+ using modern HTTP module that comes with it.
Update:
Please ensure you properly bind to template. Here is example:
https://stackblitz.com/edit/ionic-wqrnl4
I skipped the rest call (http), but the principle is the same.

What is correct way to respond from webhook running nodejs?

Trying to implement web-hook (with V2 dialogflow) running nodejs. Received response "MalformedResponse 'final_response' must be set.". Below is the code. To the end of POST (app.post) code block was expecting conv.close would send SimpleResponse. But that's not happening. Need help understand why this error is seen and probable direction to solve it.
Thanks
const express = require('express');
const {
dialogflow,
Image,
SimpleResponse,
} = require('actions-on-google')
const bodyParser = require('body-parser');
const request = require('request');
const https = require("https");
const app = express();
const Map = require('es6-map');
// Pretty JSON output for logs
const prettyjson = require('prettyjson');
const toSentence = require('underscore.string/toSentence');
app.use(bodyParser.json({type: 'application/json'}));
// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));
// http://expressjs.com/en/starter/basic-routing.html
app.get("/", function (request, response) {
console.log("Received GET request..!!");
//response.sendFile(__dirname + '/views/index.html');
response.end("Response from my server..!!");
});
// Handle webhook requests
app.post('/', function(req, res, next) {
console.log("Received POST request..!!");
// Log the request headers and body, to aide in debugging. You'll be able to view the
// webhook requests coming from API.AI by clicking the Logs button the sidebar.
console.log('======Req HEADERS================================================');
logObject('Request headers: ', req.headers);
console.log('======Req BODY================================================');
logObject('Request body: ', req.body);
console.log('======Req END================================================');
// Instantiate a new API.AI assistant object.
const assistant = dialogflow({request: req, response: res});
// Declare constants for your action and parameter names
//const PRICE_ACTION = 'price'; // The action name from the API.AI intent
const PRICE_ACTION = 'revenue'; // The action name from the API.AI intent
var price = 0.0
// Create functions to handle intents here
function getPrice(assistant) {
console.log('** Handling action: ' + PRICE_ACTION);
let requestURL = 'https://blockchain.info/q/24hrprice';
request(requestURL, function(error, response) {
if(error) {
console.log("got an error: " + error);
next(error);
} else {
price = response.body;
logObject('the current bitcoin price: ' , price);
// Respond to the user with the current temperature.
//assistant.tell("The demo price is " + price);
}
});
}
getPrice(assistant);
var reponseText = 'The demo price is ' + price;
// Leave conversation with SimpleResponse
assistant.intent(PRICE_ACTION, conv => {
conv.close(new SimpleResponse({
speech: responseText,
displayText: responseText,
}));
});
}); //End of app.post
// Handle errors.
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send('Oppss... could not check the price');
})
// Pretty print objects for logging.
function logObject(message, object, options) {
console.log(message);
console.log(prettyjson.render(object, options));
}
// Listen for requests.
let server = app.listen(process.env.PORT || 3000, function () {
console.log('Your app is listening on ' + JSON.stringify(server.address()));
});
In general, The "final_response" must be set error is because you didn't send anything back. You have a lot going on in your code, and while you're on the right track, there are a few things in the code that could be causing this error.
First - in the code, it looks like you are confused about how to send a response. You have both a call to conv.close() and the commented out assistant.tell(). The conv.close() or conv.ask() methods are the way to send a reply using this version of the library. The tell() method was used by a previous version and is no longer supported.
Next, your code looks like it is only setting up the assistant object when the routing function is called. While this can be done, it is not the usual way to do it. Typically you'll create the assistant object and setup the Intent handlers (using assistant.intent()) as part of the program initialization. This is a rough equivalent to setting up the express app and the routes for it before the request itself comes in.
The portion that sets up the Assistant and then hooks it into a route might look something like this:
const assistant = dialogflow();
app.post('/', assistant);
If you really wanted to examine the request and response objects first, you might do this as something like
const assistant = dialogflow();
app.post('/', function( req, res ){
console.log(JSON.stringify(req.body,null,1));
assistant( req, res );
});
Related to this appears to be that you're trying to execute code in the route handler and then trying to call the intent handler. Again, this might be possible, but isn't the suggested way to use the library. (And I haven't tried to debug your code to see if there are problems in how you're doing it to see if you're doing it validly.) More typical would be to call getPrice() from inside the Intent handler instead of trying to call it from inside the route handler.
But this leads to another problem. The getPrice() function calls request(), which is an asynchronous call. Async calls are one of the biggest problems that causes an empty response. If you are using an async call, you must return a Promise. The easiest way to use a Promise with request() is to use the request-promise-native package instead.
So that block of code might look something (very roughly) like this:
const rp = require('request-promise-native');
function getPrice(){
return rp.get(url)
.then( body => {
// In this case, the body is the value we want, so we'll just return it.
// But normally we have to get some part of the body returned
return body;
});
}
assistant.intent(PRICE_ACTION, conv => {
return getPrice()
.then( price => {
let msg = `The price is ${price}`;
conv.close( new SimpleResponse({
speech: msg,
displayText: msg
});
});
});
The important thing to note about both getPrice() and the intent handler are that they both return a Promise.
Finally, there are some odd aspects in your code. Lines such as res.status(500).send('Oppss... could not check the price'); probably won't do what you think they will do. It won't, for example, send a message to be spoken. Instead, the Assistant will just close the connection and say that something went wrong.
Many thanks to #Prisoner. Below is the V2 working solution based on above comments. Same has been verified on nodejs webhook (without firebase). V1 version of the code was referenced from https://glitch.com/~aog-template-1
Happy coding..!!
// init project pkgs
const express = require('express');
const rp = require('request-promise-native');
const {
dialogflow,
Image,
SimpleResponse,
} = require('actions-on-google')
const bodyParser = require('body-parser');
const request = require('request');
const app = express().use(bodyParser.json());
// Instantiate a new API.AI assistant object.
const assistant = dialogflow();
// Handle webhook requests
app.post('/', function(req, res, next) {
console.log("Received POST request..!!");
console.log('======Req HEADERS============================================');
console.log('Request headers: ', req.headers);
console.log('======Req BODY===============================================');
console.log('Request body: ', req.body);
console.log('======Req END================================================');
assistant(req, res);
});
// Declare constants for your action and parameter names
const PRICE_ACTION = 'revenue'; // The action name from the API.AI intent
var price = 0.0
// Invoke http request to obtain blockchain price
function getPrice(){
console.log('getPrice is invoked');
var url = 'https://blockchain.info/q/24hrprice';
return rp.get(url)
.then( body => {
// In this case, the body is the value we want, so we'll just return it.
// But normally we have to get some part of the body returned
console.log('The demo price is ' + body);
return body;
});
}
// Handle AoG assistant intent
assistant.intent(PRICE_ACTION, conv => {
console.log('intent is triggered');
return getPrice()
.then(price => {
let msg = 'The demo price is ' + price;
conv.close( new SimpleResponse({
speech: msg,
}));
});
});
// Listen for requests.
let server = app.listen(process.env.PORT || 3000, function () {
console.log('Your app is listening on ' + JSON.stringify(server.address()));
});