I have a local Java Service that exposes a single endpoint where the response can take over 5min sometimes depending on the request size. I want to "hide" this endpoint behind Strapi, so that a user that is not authenticated through Strapi, can't access this endpoint. What i've done is, created a custom Route in Strapi, and created a custom controller function for this route:
async optimizeRoute(ctx) {
try {
console.log(`${new Date()} - Optimize route started`)
const vehicles = ctx.request.body.vehicles.map(vehicle => {
return {
id: vehicle.id,
licenseplate: vehicle.licenseplate,
maxweight: vehicle.maxweight,
maxlength: vehicle.maxlength,
geojson: vehicle.geojson,
maxtime: moment(vehicle.endtime).diff(moment(vehicle.starttime), 'seconds') * 1.1
}
});
const orders = ctx.request.body.orders.map(order => {
return {
id: order.id,
weight: order.weight,
length: order.length,
geojson: order.geojson
}
});
console.log(`Using ${vehicles.length} vehicles, and ${orders.length} orders!`)
const response = await axios.post('http://127.0.0.1:9090/optimizeroutes',
{ vehicles: vehicles, orders: orders }, {timeout: 0}
);
return response.data;
} catch (error) {
ctx.throw(error.response.status, error.response);
}
}
But what happens when i do this, is Strapi closes the connection after 1-2 min to the requester before the response is returned.
Using Strapi Beta3.17.5.
Anywhere i can configure or debug to wait for the axios to return a response then return the response to the requester??
The default server timeout value is 2 min, Please check the image for reference.
So in server.js you can increase this timeout value by passing callback function
So the code will look like below
const strapi = require('strapi');
strapi(/* {...} */).start(()=>{
strapi.server.setTimeout(0);
});
setting timeout to 0 strapi will wait forever or you can add value in milliseconds.
Thank you for putting me on the right track #NS23, but i couldn't user server.js to start my server, and console logging strapi.server returned undefined.
What i did though was access strapi.server in Bootstrap.js, which then logging strapi.server, could see the timeout was changed from 120000 to 0
Related
I have an endpoint uploads images and updates a database table.
I send 3 requests to this endpoint at same time. Actually this problem happens when I send more than 2 API requests.
First request that comes to endpoint uploads images and updates database table successfully.
Second request that comes to endpoint uploads images sees database changes of first request, and updates database table successfully.
Third request that comes to endpoint uploads images, doesn't see database changes of second request, and updates database table successfully.
As a result; only database changes of first request and third request apply. Database changes of second request is not able to applied successfully or is overridden, somehow.
I use pg npm package.
Is problem in my code or in pg package.
How can this problem be solved?
Controller:
#UseStaffPermissionsGuards('upsert', 'VehicleCondition')
#ApiBody({ type: VehiclePhotoConditionInfoImageDTO })
#ApiResponse({ status: 201 })
#Post(':id/photos/:photoConditionId/image')
#ApiConsumes('multipart/form-data')
#UseInterceptors(FilesInterceptor('images'), FilesToBodyInterceptor)
async upsertImages(
#Param('id') vehicleId: string,
#Param('photoConditionId') photoConditionId: string,
#Body() vehiclePhotoConditionInfoImages: VehiclePhotoConditionInfoImageDTO,
): Promise<void> {
return this.vehiclePhotoConditionService.upsertImages(
vehicleId,
photoConditionId,
vehiclePhotoConditionInfoImages,
);
}
Service:
async upsertImages(
vehicleId: string,
vehiclePhotoConditionId: string,
vehiclePhotoConditionImage: VehiclePhotoConditionInfoImageDTO,
): Promise<void> {
await this.isVehicleExist(vehicleId);
const vehiclePhotoCondition = await this.getOne(vehicleId, vehiclePhotoConditionId);
if (!vehiclePhotoCondition) {
throw new BadRequestException(
`The vehicle photo condition ${vehiclePhotoConditionId} is not found`,
);
}
const imageKeys = await this.handleImages(vehiclePhotoConditionId, vehiclePhotoConditionImage);
const updatedVehiclePhotoConditions = vehiclePhotoCondition.info.map((data) => {
if (data.vehiclePart === vehiclePhotoConditionImage.vehiclePart) {
data.uploadedImagesKeys.push(...imageKeys);
}
return data;
});
const query = sql
.update('vehicle_photo_condition', {
info: JSON.stringify(updatedVehiclePhotoConditions),
updated_at: sql('now()'),
})
.where({ id: vehiclePhotoConditionId });
await this.db.query(query.toParams());
}
I solved the problem. I am posting correct code.
Here is the explanation:
In previous code, I was updating inner jsonb array of objects in code and because of the fact that the below code took some time and asynchronicity of the NodeJS, previous request can take more time than the other requests that will come later and this situation can cause data inconsistency.
Here is the previous code:
const updatedVehiclePhotoConditions = vehiclePhotoCondition.info.map((data) => {
if (data.vehiclePart === vehiclePhotoConditionImage.vehiclePart) {
data.uploadedImagesKeys.push(...imageKeys);
}
return data;
});
In current code, I am updating inner jsonb array of object in database and let database do this operation. So, no data consistency happened.
Here is current code:
const query = {
text: `
UPDATE vehicle_photo_condition
SET info = s.json_array
FROM (
SELECT
jsonb_agg(
CASE WHEN obj ->> 'vehiclePart' = $1 THEN
jsonb_set(obj, '{uploadedImagesKeys}', $2)
ELSE obj END
) as json_array
FROM vehicle_photo_condition, jsonb_array_elements(info) obj WHERE id = $3
) s WHERE id = $3`,
values: [
vehiclePhotoConditionImage.vehiclePart,
JSON.stringify(imageKeys),
vehiclePhotoConditionId,
],
};
async upsertImages(
vehicleId: string,
vehiclePhotoConditionId: string,
vehiclePhotoConditionImage: VehiclePhotoConditionInfoImageDTO,
): Promise<void> {
const vehiclePhotoCondition = await this.getOne(vehicleId, vehiclePhotoConditionId);
if (!vehiclePhotoCondition) {
throw new BadRequestException(
`The vehicle photo condition ${vehiclePhotoConditionId} is not found`,
);
}
const imageKeys = await this.handleImages(vehiclePhotoConditionId, vehiclePhotoConditionImage);
const query = {
text: `
UPDATE vehicle_photo_condition
SET info = s.json_array
FROM (
SELECT
jsonb_agg(
CASE WHEN obj ->> 'vehiclePart' = $1 THEN
jsonb_set(obj, '{uploadedImagesKeys}', $2)
ELSE obj END
) as json_array
FROM vehicle_photo_condition, jsonb_array_elements(info) obj WHERE id = $3
) s WHERE id = $3`,
values: [
vehiclePhotoConditionImage.vehiclePart,
JSON.stringify(imageKeys),
vehiclePhotoConditionId,
],
};
await this.db.query(query);
}
I've finished writing my first Cypress test. Everything is good except I'm struggling to post the result data to a website. Because I want to send the result data and also if any errors occurs the result screenshot to our coworker telegram group.
For the last two days I've tried everything and couldn't find any solution.
I've tried those in my test script (cypress/integration/test.js);
Cypress.on('test:after:run', (test, runnable) => {
console.log('test,runnable', test, runnable)
const details = {
projectKey: Cypress.env('zephyr-project-key'),
testName: test.invocationDetails.relativeFile,
status: test.status,
error: runnable.err.message,
retries: runnable.retries.length,
duration: test.wallClockDuration,
startTime: test.wallClockStartedAt
}
cy.request('POST', 'http://mywebsite.com/notify.php', { body: details })
fetch('http://mywebsite.com/notify.php')
})
Also this didn't work (cypress/plugins/index.js);
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('after:run', (results) => {
if (results) {
// results will be undefined in interactive mode
console.log(results.totalPassed, 'out of', results.totalTests, 'passed')
fetch('http://mywebsite.com/notify.php');
}
})
}
Edit: This is day 3 and I still couldn't solve this. What I've seen from Cypress help page is that cy.task() calls do not fire in 'test:after:run' event block;
https://github.com/cypress-io/cypress/issues/4823
I've seen some telegram groups who can do what I'm trying to do. All I need is to be able to get the results and post it to my website.
The third parameter to cy.request() is body, you don't have to wrap it.
Cypress.on('test:after:run', (test, runnable) => {
const details = {
projectKey: Cypress.env('zephyr-project-key'),
testName: test.invocationDetails.relativeFile,
status: test.status,
error: runnable.err?.message, // need err?.message if there is no error
retries: runnable.retries.length,
duration: test.wallClockDuration,
startTime: test.wallClockStartedAt
}
cy.request('POST', 'http://mywebsite.com/notify.php', details) // don't wrap details
.then(res => expect(res.status).to.eq(201)) // confirm result
})
I need to continuously make this http.get request from an API that sends back location data
So i tried writing a basic get reuest to check if the data is coming through, and it is, the problem is I need it in a continuous loop every second the gps device sends data.
http.get(_url2, res =>{
let body='';
res.on("data", data =>{
body+=data;
})
res.on("end",()=>{
body = JSON.parse(body);
gpsData.push(body.data);
console.log('gpsData Array : ',gpsData)
})
})
I get this logged out to the console,
$ node server3.js
gpsData Array : [ [ { imei: '86851212020143921',
device_info: 0,
device_info_new: 0,
seconds: 0,
gps_time: 1548760740,
sys_time: 1548760744,
heart_time: 1548760744,
server_time: 1548760748,
lng: 33.756899,
lat: -13.973598,
course: 160,
speed: 7,
status: '010000fb000000000000000000000000',
location: 'GPS',
acc: '1',
acc_seconds: 335,
voice_status: -1,
voice_gid: 0 } ] ]
showing that it's working
how can I make this code into an asynchronous WebSocket that continuously gets that data, then stores it in MongoDB with given fields?
ok so I found a solution, the best(in my opinion) way is to use setInterval() function, put the HTTP request inside the function to set how many times per unit time you want to insert in the db, then from there mongodb accepts objects by default, so if you return an object to mongodb is will save it for you, here's the code
MongoClient.connect('mongodb://127.0.0.1:27017/gpsapp2', function(err,client){
if(err) return console.log(err)
db = client.db('gpsapp2')
let counter =0
setInterval(()=>{
http.get(_url2, res =>{
let body='';
res.on("data", data =>{
body+=data;
})
res.on("end",()=>{
body = JSON.parse(body);
//console.log(counter++,'Records inserteerd')
db.collection('gpsdata').save(body.data[0]);
})
})
},1000);
http.get from the url, parse the body then save to collection. dont forget the API call is asynchronous so any call for the data outside res.on('end') will get empty or undefined results.
hope this helps somebody.
I am trying to create a API using nodejs and access it using GET method by sending parameters using Angular 5 GET method. When I am testing my API using Postman, it works fine, but sending the GET request from Angular is not giving me the result. My node js router for receiving multiple parameters code is as follow:
router.get('/:min&:max',(req,res,next)=> {
Image.find({hue: {$gt:req.params.min,$lt:req.params.max}})
.select('name url hue')
.exec()
.then(docs => {
const response={
images: docs.map(doc=> {
return {
name: doc.name,
url: doc.url,
hue: doc.hue,
_id: doc._id,
}
})
}
res.status(200).json(docs);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
})
My angular GET method goes like this :
getSearchedImages(min, max) {
let params = {
'min': String(min),
'max': String(max)
}
this.http.get('http://localhost:3000/', { params: params})
.subscribe(val=> console.log(val))
}
Is there any problem in Angular part or is my code in Node is to be adjusted
Use POST if you want to pass parameters in request body. Otherwise, if you like GET, pass parameters in URL.
HTTP itself doesnt restrict this, but some front-end implementations do
I am using an express server with GraphQL subscriptions and subscriptions-transport-ws.
I have set up the subscription with a given channel:
...
const subscriptionManager = new SubscriptionManager({
schema: executableSchema,
pubsub: pubsub,
setupFunctions: {
testRunChanged: (options, args) => {
return {
testRunChangedChannel: {
filter: (testRun) => {
return testRun.id === args.testRunId;
}
},
};
},
},
});
...
After a mutation is received a process is started on the server where the database entry of the test run is updated when finished. Now when the update promise for the database action passes the client should be informed.
Using the publish functionality from pubsub the subscription manager gets the information about the updated test run:
...
RunningTestDbService.setToFinished(testRun).then(updatedTestRun => {
pubsub.publish("testRunChangedChannel", updatedTestRun);
})
...
After the subscription manager filters the subscriptions depending on the published testRun and the subscribed testRunId the subscription resolver function is called. To update the client i have to fetch the updated test run again.
How can i get the published test run object inside of the subscription resolver function?
The subscription and the resolver look like this:
...
`testRunChanged(testRunId: ID!): TestRun!`
...
Subscription: {
testRunChanged(_, { testRunId }) {
// need to fetch the test run from database again
return TestRunDbService.getTestRunWith(testRunId);
},
},
...
The object used in publish method as payload is then root parameter of your subscription resolver method - so in this case this is _ in your testRunChanged resolver function. You should simply do return _.