How to receive Post request body and pass that body to my function in loopback - loopbackjs

i want to create dynamic model, repository and controller
export async function dynamicModelsDemo(app: any, modelData: any): Promise<boolean> {
console.log("ModelData",modelData);
// assume that this def can be created dynamically (at runtime), e.g. from database info
const modelDef = new ModelDefinition({
name: 'contact',
properties: {
id: {
type: 'Number',
required: true,
length: null,
precision: 10,
scale: 0,
id: 1,
},
name: {
type: 'String',
required: false,
length: 512,
precision: null,
scale: null,
},
},
});
// tryin' to extend Entity with new fields
const DynamicModel = defineModelClass<typeof Entity, {id: number; title?: string}>(
Entity,
modelDef,
);
const BookRepository = defineCrudRepositoryClass(DynamicModel);
inject(`datasources.memory`)(BookRepository, undefined, 0);
const repoBinding = app.repository(BookRepository);
const basePath = '/contact';
const DynamicController0 = defineCrudRestController(DynamicModel, {basePath});
inject(repoBinding.key)(DynamicController0, undefined, 0);
app.controller(DynamicController0);
console.log(basePath);
return new Promise(function (resolve, reject) {
resolve(true);
});
}
i need help that how should i create Post method which would receive request body and that body would pass to my function above i mentioned,
Currently i'm calling dynamicModelsDemo function by this endpoint,
#get('/ping/build', {
modelData : {},
responses: {
'200': {
description: 'Test models assemble',
},
},
})
async build(): Promise<boolean> {
return dynamicModelsDemo(this.localApp,this.modelData);
}
i want to convert this #get to #post so i can pass my requested body to this function..

This is working so fine, I thing this is what I was looking for:
#post('ping/createobject')
async createObject(
#requestBody() model: any
):Promise<boolean> {
return dynamicModelsDemo(this.localApp,model);
}

Related

Mirage server GETs data but POST fails

I have the mirage models:
// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';
export default Model.extend({
name: '',
iso3166_1_alpha3: '',
capitol_city: belongsTo('city', {inverse: null}),
cities: hasMany('city', {inverse: 'country'})
});
and:
// mirage/models/city.js
import { Model, belongsTo } from 'miragejs';
export default Model.extend({
name: '',
country: belongsTo('country', {inverse: 'cities'})
});
and the serializer:
// mirage/serializers/application.js
import { camelize, capitalize, underscore } from '#ember/string';
import { JSONAPISerializer } from 'miragejs';
export default class ApplicationSerializer extends JSONAPISerializer
{
alwaysIncludeLinkageData = true;
keyForAttribute(attr) {
return underscore(attr);
};
keyForRelationship(modelName) {
return underscore(modelName);
};
typeKeyForModel(model) {
return capitalize(camelize(model.modelName));
};
};
When I run the tests:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
module('Unit | Mirage | mirage models', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('it retrieves the country', async function (assert) {
const server = this.server;
let city = server.create('city', { id: '1', name: 'Paris' });
server.create(
'country',
{
id: 'FR',
name: 'France',
iso3166_1_alpha3: 'FRA',
capitol_city: city
}
);
let response = await fetch('/api/countries')
assert.strictEqual(response.status, 200, "Should have created the model");
let json = await response.json();
assert.deepEqual(
json,
{
data: [
{
type: 'Country',
id: 'FR',
attributes: {
name: 'France',
iso3166_1_alpha3: 'FRA',
},
relationships: {
capitol_city: {data: {type: 'City', id: '1'}},
cities: {data: []},
}
}
]
}
)
});
test('it creates the country', async function (assert) {
const server = this.server;
server.create('city', { id: '1', name: 'Paris' });
let response = await fetch(
'/api/countries',
{
method: 'POST',
headers: {'Countent-Type': 'application/json'},
body: JSON.stringify(
{
data: {
id: 'FR',
type: 'Country',
attributes: {
iso3166_1_alpha3: 'FRA',
name: 'France',
},
relationships: {
capitol_city: { data: { type: 'City', id: '1'} },
cities: { data: [{ type: 'City', id: '1'}] }
}
}
}
)
}
);
console.log((await response.json()).message);
assert.strictEqual(response.status, 201, "Should have created the model");
});
});
The first one passes and the second one fails with the message:
Mirage: You're passing the relationship 'capitol_city' to the 'country' model via a POST to '/api/countries', but you did not define the 'capitol_city' association on the 'country' model.
How can I get Mirage to recognise the capitol_city attribute on the model?
Mirage is opinionated with regards to the format of attributes and expects the attributes to be in camelCase (and not snake_case).
Unfortunately the Ember CLI Mirage model relationships documentation does not mention this expectation and all the examples use single-word attributes. Even more unfortunately, Mirage will work with snake_case attributes for simple GET requests and when directly creating models through the API; it is only when you make a request to POST/PUT/PATCH a model into the server that it fails and the message will (confusingly) refer to the snake case attribute which has been defined. (See the Mirage source code for where it fails.)
To solve it, convert the attributes to camel case:
// mirage/models/country.js
import { Model, belongsTo, hasMany } from 'miragejs';
export default Model.extend({
name: '',
iso31661Alpha3: 0,
capitolCity: belongsTo('city', {inverse: null}),
cities: hasMany('city', {inverse: 'country'})
});
and change it in the tests as well:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
module('Unit | Mirage | mirage models', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('it retrieves the country', async function (assert) {
const server = (this as any).server;
let city = server.create('city', { id: '1', name: 'Paris' });
server.create(
'country',
{
id: 'FR',
name: 'France',
iso31661Alpha3: 'FRA',
capitolCity: city
}
);
let response = await fetch('/api/countries')
assert.strictEqual(response.status, 200, "Should have created the model");
let json = await response.json();
console.log(JSON.stringify(json));
assert.deepEqual(
json,
{
data: [
{
type: 'Country',
id: 'FR',
attributes: {
name: 'France',
iso3166_1_alpha3: 'FRA',
},
relationships: {
capitol_city: {data: {type: 'City', id: '1'}},
cities: {data: []},
}
}
]
}
)
});
test('it creates the country', async function (assert) {
const server = (this as any).server;
let city = server.create('city', { id: '1', name: 'Paris' });
let response = await fetch(
'/api/countries',
{
method: 'POST',
headers: {'Countent-Type': 'application/json'},
body: JSON.stringify(
{
data: {
id: 'FR',
type: 'Country',
attributes: {
iso3166_1_alpha3: 'FRA',
name: 'France',
},
relationships: {
capitol_city: { data: { type: 'City', id: '1'} },
cities: { data: [{ type: 'City', id: '1'}] }
}
}
}
)
}
);
console.log((await response.json()).message);
assert.strictEqual(response.status, 201, "Should have created the model");
});
});
However, once you convert it to camel case then the attribute iso31661Alpha3 does not get formatted correctly in the output so you have to manually change the serializer for the country model:
// mirage/serializers/country.js
import ApplicationSerializer from './application';
export default class CountrySerializer extends ApplicationSerializer
{
keyForAttribute(attr: string) {
switch(attr)
{
case 'iso31661Alpha3': return 'iso3166_1_alpha3';
default: return super.keyForAttribute(attr);
}
};
};
Once the attributes are in the correct case then it will work.

How to res.send something in loopback-next

I have a function that has a callback as shown below, and I want to return account that is returned in the callback as a response to the request for the function. How could I res.send the account (since I cannot return values from a callback function)
#get('/payments/retrieve-stripe/{id}', {
responses: {
'200': {
description: 'User model instance',
content: {'application/json': {schema: {'x-ts-type': User}}},
},
},
})
async retrieveStripe(#param.path.number('id') id: number,
#requestBody() req: any): Promise<any> {
if (!req.stripeAccountId) {
throw new HttpErrors.NotFound('No Stripe Account');
}
else {
stripe.accounts.retrieve(
req.stripeAccountId,
async function(err: any, account: any) {
//console.log(err)
console.log(account)
return account
})
}
}
If you're stuck using a callback any any point in your code you're going to use manual promises (or maybe some promise wrapping library).
Instead of using async and return, use resolve() which functionally can return from any point in your function, regardless of scope.
#get('/payments/retrieve-stripe/{id}', {
responses: {
'200': {
description: 'User model instance',
content: {'application/json': {schema: {'x-ts-type': User}}},
},
},
})
retrieveStripe(#param.path.number('id') id: number, #requestBody() req: any): Promise<any> {
return new Promise((resolve, reject) => {
if (!req.stripeAccountId) {
throw new HttpErrors.NotFound('No Stripe Account');
}
else {
stripe.accounts.retrieve(req.stripeAccountId, function(err: any, account: any) {
resolve(account);
})
}
});
}

How to call remote method of one persisted model inside another remote method of another persisted model

here's what I tried in model1.js:
model1.remotemethod1 = function(id, data, cb) {
var model2 = app.models.model2;
model2.remotemethod2(id, data).then(response => {
cb(null, true);
});
};
this is my model2.js :
it has the definition of remotemethod2 .
'use strict';
module.exports = function(model2) {
model2.remotemethod2 = function(id, data, cb) {
var promise;
let tags = data.tags ? data.tags.slice() : [];
delete data.categories;
delete data.tags;
promise = model2.upsertWithWhere({
or: [
{barcode: data.barcode},
{id: data.id},
],
}, data);
promise.then(function(model2) {
model2.tags.destroyAll().then(function() {
for (let i = 0; i < tags.length; i++) {
model2.tags.add(tags[i]);
}
cb(null, model2);
});
});
};
};
But it dos not work !
I think that app.models.model2 does not give me the model with its remote methods ! maybe I should get an instance of the model2 !
var (VAR-NAME)= (CURRENT-MODEL).app.models.(ANOTHER_MODEL);
you now can use the other model by calling one of its methods for example
EX:
VAR-NAME.create();
Declare remotemethod1 in server.js app.start and you'll have access to the correct app.models.model2 and you will be able to use its remote method.
app.start = function() {
model1.remotemethod1 = (id, data, cb) => {
var model2 = app.models.model2;
model2.remotemethod2(id, data).then(response => {
cb(null, true);
});
};
model1.remoteMethod(
'remotemethod1', {
http: { path: '/remotemethod1', verb: 'post', status: 200, errorStatus: 400 },
accepts: [{arg: 'id', type: 'number'}, {arg: 'id', type: 'object'}],
returns: {arg: 'status', type : 'string' }
}) ;
}
// The rest of app.start...
EDIT you can also create the remote method will the correct app context with a file located in myprojectname/server/boot
`module.exports(app) {
/* Create remote methods here */
}`

tag-it :How to diss allow free text

http://aehlke.github.io/tag-it/
What should i do to avoid free text on tag-it?
I mean user should be able to tag only those strings suggested by auto compelte
$("#selector").tagit({
// Options
fieldName: "projects",
autocomplete: {
minLength: 2,
source: function (request, response) {
$.ajax({
url: '/xxx/xxxx',
type: 'POST',
data: {
searchKey: request.term
},
success: function (data) {
response($.map(data, function (item) {
return { label: item.Name };
}));
}
});
}
},
showAutocompleteOnFocus: false,
removeConfirmation: false,
caseSensitive: false
});
I suggest to somehow combine autocomplete with beforeTagAdded which discards tag from being added by returning false:
$("#selector").tagit({
//...
beforeTagAdded: function(event, ui) {
return isSuggested(ui.tagLabel);
}
});

Correct usage of store.loadMany() function

I'm trying to figure out how to populate a table from a JSON object.
My JSON is a structurated object:
{
id: 0,
list: [{ username:'user_1',online:true, user:0 },
{ username:'user_2',online:true, user:0 }]
}
My Model is defined as follow:
MyTalk.WUser = DS.Model.extend({
list: DS.hasMany('MyTalk.User')
});
MyTalk.User = DS.Model.extend({
username: DS.attr('string'), // primary key
online: DS.attr('boolean'),
user: DS.belongsTo('MyTalk.WUser')
});
I am using a custom Adapter for ember-data:
DS.SocketAdapter = DS.RESTAdapter.extend(MyTalk.WebSocketConnection, {
// code not relevant
}
DS.SocketAdapter.map('MyTalk.WUser', {
list: {embedded: 'always'}
});
DS.SocketAdapter.map('MyTalk.User', {
primaryKey: 'username'
});
MyTalk.Store = DS.Store.extend({
revision: 12,
adapter: DS.SocketAdapter.create({})
});
Now I would load my data. I run in Chrome command line the following statements:
var store = DS.get('defaultStore');
var obj = {
id: 0,
list: [{ username:'user_1',online:true, user:0 },
{ username:'user_2',online:true, user:0 }]
};
var store.loadMany(MyTalk.WUser,obj);
var record = MyTalk.WUser.find(0);
record.serialize();
But it returns no record:
> Object {list: Array[0]}
thanks in advance!!
If you want to allow the adapter to deserialize embedded records (or perform any custom deserialization, for that matter), you'll need to load your data through the adapter rather than directly into the store.
var store = DS.get('defaultStore'),
obj = {
id: 0,
list: [{ username:'user_1', online:true, user:0 },
{ username:'user_2', online:true, user:0 }]
},
type = MyTalk.WUser,
adapter = store.adapterForType(type);
adapter.load(store, type, obj);