Custom handler in Strapi - customization

I'm trying to create a custom route and handler for user-permissions but it's not working. Not sure though if my method is wrong or cannot be done. By the way here is my code.
/config/routes.json
{
"routes": [
{
"method": "PUT",
"path": "/users/:id/approve",
"handler": "users.approve",
"config": {
"policies": []
}
}
]
}
/controllers/Users.js
module.exports = {
approve: async ctx => {
const { id } = ctx.params;
console.log('approve user')
}
};
From routes, the handler should read the approve in controller but the strapi threw error TypeError: Cannot read property 'approve' of undefined

Related

Ember Data Serializer & Adapter with Supabase returns empty (Proxy { })

Struggling to create my customised adapter & serializer to integrate Supabase, how I'm stuck why no data in Ember Data.
Trying out with a simple findAll() method. See below:
Service ⬇️:
export default class SupabaseService extends Service {
client;
constructor() {
super(...arguments);
const { url, key } = ENV.supabase;
const supabase = createClient(url, key);
this.client = supabase;
}
}
Model ⬇️:
export default class CourseModel extends Model {
#attr('string') name;
#attr('date') date_added;
}
Adapter ⬇️:
export default class ApplicationAdapter extends RESTAdapter {
#service supabase;
async findAll(store, type, neverSet, snapshotRecordArray) {
return new Promise(async (resolve, reject) => {
try {
const { data, error, status } = await this.supabase.client
.from(pluralize(type.modelName))
.select('*');
if (error) {
reject(error);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
});
}
}
Serializer ⬇️:
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// parse the response data from the server and return it in the format that Ember Data expects
let newPayload = {
data: payload.map(item => {
let attributes = JSON.parse(JSON.stringify(item));
delete attributes.id;
return {
id: item.id,
type: primaryModelClass.modelName,
attributes: attributes
}
})
}
return super.normalizeResponse(store, primaryModelClass, newPayload, id, requestType);
}
✅ The service works fine. The adapter manage to get data and returns as follows:
[
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"date_added": "2022-12-31T00:03:14.618585+00:00",
"name": "Science"
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"date_added": "2022-12-31T00:03:30.010963+00:00",
"name": "Physics"
}
]
The serializer newPayload to follow JSON API schema, returns:
{
"data": [
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"type": "course",
"attributes": {
"name": "Science",
"date_added": "2022-12-31T00:03:14.618585+00:00"
}
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"type": "course",
"attributes": {
"name": "Physics",
"date_added": "2022-12-31T00:03:30.010963+00:00"
}
}
]
}
But the problem is no data in store. Logging model in template shows empty Proxy {}.
I have no idea why. Ember Inspector shows no model in Data.
Any suggestions?

Bypass custom payload from Whatsapp API or custom integration to dialogflow ES API

I use a Dialogflow API as NLP and the interface that we use is Whatsapp API.
my problem is, when I want to bypass Text and Whatsapp client number to Dialogflow (my reference), I didn't found document to explain that. for comparison, the Telegram official integration dialogflow, from the body request we can extract that data like name and Telegram user ID.
const sessionId = phone_number_id; //session ID get from phone number
const sessionPath = sessionClient.projectAgentSessionPath(projectId, sessionId);
const request = {
session: sessionPath,
queryInput: {
text: {
text: msg_body,
languageCode: "id-ID"
},
},
payload: {
data: "testing",
phoneNumber : phone_number_id
}
};
console.log("request", request);
await sessionClient.detectIntent(request).then(responses => {
console.log("DetectIntent", JSON.stringify(responses));
}).catch(err => {
console.error("ERROR:", err);
})
I tried it with request variable like that but in request body in dialogflow fulfillment, it never showed up
{
"responseId": "censored",
"queryResult": {
"queryText": "halo",
"action": "input.welcome",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentText": "error",
"fulfillmentMessages": [
{
"text": {
"text": [
"error"
]
}
}
],
"outputContexts": [
{
"name": "censored",
"parameters": {
"no-input": 0,
"no-match": 0
}
}
],
"intent": {
"name": "censored",
"displayName": "Default Welcome Intent"
},
"intentDetectionConfidence": 1,
"languageCode": "id"
},
"originalDetectIntentRequest": {
"payload": {}
},
"session": "censored"
}
#Maulana ahmad, As you have mentioned in the comment below example code can be referred to extract data from the body request.
const dialogflow = require('dialogflow');
// Import the JSON to gRPC struct converter
const structjson = require('./structjson.js');
// Instantiates a sessison client
const sessionClient = new dialogflow.SessionsClient();
// The path to identify the agent that owns the created intent.
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
session: sessionPath,
queryInput: {
event: {
name: eventName,
parameters: structjson.jsonToStructProto({foo: 'bar'}),
languageCode: languageCode,
},
},
};
sessionClient
.detectIntent(request)
.then(responses => {
console.log('Detected intent');
logQueryResult(sessionClient, responses[0].queryResult);
})
.catch(err => {
console.error('ERROR:', err);
});
This Stack Overflow link can be referred for more information.
Posting the answer as community wiki for the benefit of the community that might encounter this use case in the future.
Feel free to edit this answer for additional information.

Postman - How to replace values for all specific properties in the JSON Response, to use it later as another Request's Body

I have 2 API requests. 1st one is GET, that returns a response. This response is used as a Body/Payload in the 2nd request (POST). BUT the Payload should have certain values to be replaced before used in the 2nd request (in my case below it should be value for "Status" property).
How can I do it?
Here is my example Response:
{
"Variations":[
{
"ItemIds":[
"xxx"
],
"Items":[
{
"Id":"67-V1",
"GuId":"xxx",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 1",
"Id":"67-V1"
},
{
"ItemIds":[
"yyy"
],
"Items":[
{
"Id":"67-V2",
"GuId":"yyy",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 2",
"Id":"67-V2"
},
{
"ItemIds":[
"zzz"
],
"Items":[
{
"Id":"67-V3",
"GuId":"zzz",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 3",
"Id":"67-V3"
}
],
"ItemIds":[
],
"Items":[
],
"Name":"MAINP",
"Id":"67",
"Color":null
}
Here is my code, but it does not seem to work (the replacement part):
var jsonData = pm.response.json();
function replaceStatus() {
_.each(jsonData.Variations, (arrayItem) => {
if(arrayItem.Items.Status !== "NonActive") {
arrayItem.Items.Status == "NonActive";
console.log("arrayItem " + arrayItem);
}
});
}
pm.test("Run Function", replaceStatus ());
pm.sendRequest({
url: 'localhost:3000/post',
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: {
mode: 'raw',
raw: JSON.stringify(jsonData)
}
}, (err, res) => {
console.log(res)
})
I guess you are trying to replace all NonActive values with Active. In that case, you should use = for assignment not ==. The JSON file you provided is not valid and couldn't run your code on my machine. I am happy to have a closer look if that didn't work
Based on your updates these changes need to be made
1- in order to deal with JSON object you need to parse the response as it is string and you can't call sth like JsonData.Variations on that.make sure jsonData is a JSON object. if not add sth like this to parse it
var parsedJson = JSON.parse(jsonData)
2- It seems that you missed one array layer in your function to iterate over items. As you have two nested arrays to reach Status the replaceStatus function should be as below
function replaceStatus() {
_.each(parsedJson.Variations, (arrayItem) => {
_.each(arrayItem.Items, (item) => {
if(item.Status !== "NonActive") {
item.Status = "NonActive";
console.log("arrayItem " + item.Status);
}
});
});
}
Have you posted your entire code in the tests section or only a part of it?
I saw from one of your comments that you cannot see the output logged to the console.
This may be very trivial, but, if you did post your entire code, it looks like you may have forgotten to call your replaceStatus() function before your post call.

How to intercept every not asset calls to constantly render the index.html file

I have built an SPA Application, i need to deploy it on a loopback server, and i never used it, so i am lost with all of these json files you can configure to intercept the calls.
I simply need to intercept every calls (expect static files) and to return my index.html file for every cases.
Technically, it is just a middleware that is invocked after the "serve-static" middleware and say "ok if you are here, you are not an asset, render the index.html file".
My / path already return the index.html, how am i supposed to implement that rule properly with loopback?
I guess it is going to happens on the middleware.json file, so mine currently looks like this:
{
"initial:before": {
"loopback#favicon": {}
},
"initial": {
"compression": {},
"cors": {
"params": {
"origin": true,
"credentials": true,
"maxAge": 86400
}
}
},
"session": {
},
"auth": {
},
"parse": {
},
"routes": {
"loopback#rest": {
"paths": ["${restApiRoot}"]
}
},
"files": {
"serve-static": {
"params": "$!../client"
}
},
"final": {
"loopback#urlNotFound": {}
},
"final:after": {
"loopback#errorHandler": {}
}
}
You can a middleware which will check if nodejs has already served a request, if not redirect to /.
//in middleware.json
"final:before": {
"./middleware/redirect.js": {},
}
//in server/middleware/redirect.js
module.exports = function() {
return function redirect(request, response, next) {
if (!response.headersSent) {
response.redirect('/');
} else {
next();
}
}
};

How to create a loopback <model>.json file from unstructured data

I can create a loopback model from an example json instance as shown here https://docs.strongloop.com/display/public/LB/Creating+models+from+unstructured+data. But from there is there an API to create the .json file in common/models?
#BoLaMN from Looback Gitter said this:
try app.registry.modelBuilder.introspect(json); that should give you a properties object. Then just add name, base to it and fs.writeSync JSON.stringify(obj).
I haven't tried yet, but it makes sense.
I've been using this:
var User = db.buildModelFromInstance('User', user, {idInjection: true});
var UserModel = (loopback.getModel(User));
var UserModelJSON = {}
UserModelJSON.name = 'User';
UserModelJSON.base = 'PersistedModel';
UserModelJSON.properties = UserModel.definition.rawProperties;
console.log(JSON.stringify(UserModelJSON));
fs.writeFile('User.json', 'UserModelJSON', function(err) {
if (err) throw err;
});
In your loopback /server directory at the root of your project, if you define the datasources.json file like so
{
"db": {
...
},
"restResourceName": {
"name": "restResourceName",
"baseURL": "http://restResourceUrl/",
"crud": true,
"connector": "rest"
}
}
And you define a boot script, like so
const fs = require('fs')
const path = require('path')
const writeInstance = (server, modelName, instance) => {
let Model = server.dataSources.restResourceName.buildModelFromInstance(modelName, instance)
let modelJson = Model.definition.toJSON()
delete modelJson.settings.base
fs.writeFile(path.join(__dirname, '..', 'models', modelName + '.json'),
JSON.stringify(modelJson, null, 2), (/*...*/) => {
/*...*/
})
}
module.exports = function (server) {
writeInstance(server, "Atom", {
atomicWeight: 6
})
}
You will generate a model file Atom.json
{
"name": "Atom",
"properties": {
"atomicWeight": {
"type": "Number"
},
"id": {
"type": "Number",
"id": 1,
"generated": true,
"updateOnly": true
}
},
"settings": {
"strict": false,
"forceId": "auto",
"replaceOnPUT": true
}
}
If you want to see the models in the loopback api explorer (swagger GUI), you must add the model to the model-config.json with a public: true property
{
"_meta": {
"sources": [
...
],
"mixins": [
...
]
},
"Atom": {
"public": true,
"dataSource": "restResourceName"
}
}