Remove nested relations from API explorer (swagger) - loopbackjs

I have Application model with following relation:
#belongsTo(() => Ido)
idoId: string;
export interface ApplicationRelations {
ido?: IdoWithRelations;
}
export type ApplicationWithRelations = Application & ApplicationRelations;
Application repository looks like this:
export class ApplicationRepository extends DefaultCrudRepository<
Application,
typeof Application.prototype.id,
ApplicationRelations
> {
public readonly user: BelongsToAccessor<
User,
typeof Application.prototype.id
>;
constructor(
#inject('datasources.db') dataSource: DbDataSource,
#repository.getter('UserRepository')
protected userRepositoryGetter: Getter<UserRepository>,
) {
super(Application, dataSource);
this.user = this.createBelongsToAccessorFor('user', userRepositoryGetter);
this.inclusionResolvers.delete('ido');
}
}
And the following relation in IDO model:
#hasMany(() => Application)
applications: Application[];
In post /ido in swagger i am getting this example for creating:
{
"applications": [
{
"status": "string",
"createdAt": 0,
"idoId": "string",
"userId": "string",
"ido": {
"applications": [
{
"status": "string",
"createdAt": 0,
"idoId": "string",
"userId": "string",
"ido": {
"applications": [
"string"
],
}
Is there any ways to remove the duplicate and kind of curricular relation for ido in application from swagger? Or this doesn't really matter and i can just manually delete all fields above the first application?

This answer was copied over from https://github.com/loopbackio/loopback-next/discussions/8536#discussioncomment-2655375.
OpenAPI/Swagger is at the REST layer, which means you'll need to look inside the respective REST Controller, which would have something similar to this:
#post('...')
function create(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Application, {
title: 'NewApplication',
exclude: ['id'],
}),
},
},
})
)
You can modify it as such to exclude relations:
#post('...')
function create(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Application, {
title: 'NewApplication',
exclude: ['id'],
+ includeRelations: false,
}),
},
},
})
)
An alternative is to use exclude.
More info can be found in the API docs: https://loopback.io/doc/en/lb4/apidocs.repository-json-schema.getjsonschemaref.html

Related

Ember Cli Mirage: Active Model Adapter with JSONAPISerializer

I am on halfway of implementing JSON API structure (with underscore attributes).
Actual state for development environment is:
I use the Active Model Adapter structure for requesting to the backend for resources and backend response me with JSON API structure.
In Application Serializer I am using JSONAPISerializer. I override methods:
serializeBelongsTo
keyForRelationship
keyForAttribute
serialize
serializeAttribute
serializeHasMany
and for development, everything works for me (backend in Rails communicate with Ember very good).
The problem is with Ember CLI Mirage and conventions (not sure if there are simple solutions or I need to override again methods in this addon).
Actual state with Ember Cli Mirage and test environment:
I am using import { JSONAPISerializer } from 'ember-cli-mirage';
and then trying to manipulate proper request and then transform it for JSON API format.
It could work like this:
Ember Adapter (Active Model Adapter format - with underscore attributes) ---> Mirage Serializer should get request (find resources created before in tests with associations) and then response it with JSON API format ---> JSON API Serializer could catch it and fill Ember DS.
For now, I have a missing part to serialize it for all cases to JSON API standard (with underscored attributes)
Where should I do this transformation to minimize overriding JSONAPISerializer Mirage Serializer.
I noticed that there are some helpers, but I have a problem to wrap this knowledge together
(http://www.ember-cli-mirage.com/docs/advanced/route-handlers#helpers)
UPDATE:
Example of structure from Backend:
{
"data": {
"id": "6",
"type": "first_resource",
"attributes": {
"id": 6,
"my_attribute": "my_attribute"
},
"relationships": {
"second_resources": {
"data": [
{
"id": "16",
"type": "second_resource"
}
]
},
"third_resource_other_type": {
"data": {
"id": "1",
"type": "third_resource"
}
},
"fourth_resource": {
"data": {
"id": "1",
"type": "fourth_resource"
}
}
},
"links": {
"fifth_resources": "/api/v1/first_resources/6/fifth_resources"
}
},
"included": [
{
"id": "1",
"type": "fourth_resource",
"attributes": {
"id": 1,
"my_attribute": "my_attribute"
},
"links": {
"sixth_resource": "/api/v1/fourth_resources/1/sixth_resource"
}
},
{
"id": "16",
"type": "second_resource",
"attributes": {
"id": 16,
"my_attribute": "my_attribute"
},
"relationships": {
"eighth_resources": {
"data": []
}
},
"links": {
"seventh_resources": "/api/v1/second_resources/16/seventh_resources"
}
},
{
"id": "17",
"type": "second_resource",
"attributes": {
"id": 17,
"my_attribute": "my_attribute"
},
"relationships": {
"eighth_resources": {
"data": []
}
},
"links": {
"seventh_resources": "/api/v1/second_resources/17/seventh_resources"
}
},
{
"id": "15",
"type": "second_resource",
"attributes": {
"id": 15,
"my_attribute": "my_attribute"
},
"relationships": {
"eighth_resources": {
"data": [
{
"id": "26",
"type": "eighth_resource"
},
{
"id": "24",
"type": "eighth_resource"
}
]
}
},
"links": {
"seventh_resources": "/api/v1/second_resources/15/seventh_resources"
}
},
{
"id": "26",
"type": "eighth_resource",
"attributes": {
"id": 26,
"my_attribute": "my_attribute"
}
}
]
}
UPDATE2
structure from mirage response:
data: {
attributes: {
my_attribute: 'my_attribute',
second_resource_ids: [36, 37],
fifth_resource_ids: []
},
id: 11,
relationships: {
third_resource_other_type: {data: null}
fourth_resource: {data: null}
second_resources: {data: []}
},
type: "first_resources"
}
resources in tests:
server.create('second-resource', {
id: 36,
first_resource_id: '11',
my_attribute: "my_attribute"
});
server.create('eighth-resource', {
id: 140,
second_resource_id: 37
});
server.create('eighth-resource', {
id: 141,
second_resource_id: 37
});
server.create('second-resource', {
id: 37,
first_resource_id: '11',
eighth_resource_ids: [140, 141]
});
server.create('first-resource', {
id: 11,
second_resource_ids: [36, 37]
});
first_resource model in mirage:
export default Model.extend({
third_resource_other_type: belongsTo(),
fourth_resource: belongsTo(),
fifth_resources: hasMany(),
second_resources: hasMany()
});
Let's try to focus a single relationship, since there's a lot going on in the question you've posted. We'll look at second-resource.
It looks like Mirage is sending back second_resource_ids under the attributes key of the first_resource primary data in the JSON:API payload. That tells me Mirage thinks second_resource_ids is an attribute of first_resource, when in fact it's a relationship.
Assuming your Models & Relationships are setup correctly, you need to tweak the way you're creating data in Mirage.
If you take a look at the Associations section of the Defining Routes guide, you'll see this message:
Mirage's database uses camelCase for all model attributes, including foreign keys (e.g. authorId in the example above)
Right now, you're doing this:
server.create('second-resource', {
id: 36,
first_resource_id: '11',
my_attribute: "my_attribute"
});
server.create('first-resource', {
id: 11,
second_resource_ids: [36, 37]
});
But from Mirage's perspective, you need to use camelCase IDs, or just pass in the relationships, to set these up correctly. Something like this:
let firstResource = server.create('first-resource', {
id: 11
});
server.create('second-resource', {
id: 36,
firstResource,
myAttribute: "my_attribute"
});
You could also pass in the foreign key on creation if you wanted - just be sure to use camelCase:
server.create('second-resource', {
id: 36,
firstResourceId: '11',
myAttribute: "my_attribute"
});
Just remember that the formatting decisions for things like attributes and foreign keys (things like some-attribute vs. some_attribute or relationship-id vs. relationship_id) are made at the serializer layer. When dealing with Mirage's ORM and database, you want to stick to camelCase, regardless of the format your Serializer emits.
For more information, have a look at these sections from the docs:
"Associations and serializers" section of the Quickstart
Defining relationships
"Working with relationships" section of the Factories guide

Load multiple model data in same api call emberjs?

So here is two models that i have defined in emberjs
match.js
import DS from 'ember-data';
export default DS.Model.extend({
team: DS.belongsTo('team', {async:true}),
opponent: DS.belongsTo('team', {async: true}),
type: DS.attr('string'),
squad: DS.attr('boolean')
});
and
team.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
logo: DS.attr('string')
});
I am already loading the match as a model. In the same api call i also want to load the model data for team. The api response that i have till now is
{
"meta":{
"type":"match"
},
"data":[
{
"id":1119536,
"type":"match",
"attributes":{
"id":1119536,
"team":{
"type":"team",
"id":1,
"attributes":{
"id":1,
"name":"England",
"logo":null
}
},
"opponent":{
"type":"team",
"id":3,
"attributes":{
"id":3,
"name":"Pakistan",
"logo":null
}
}
}
}
]
}
The match model data get loaded properly but i am having issues for the same with team data. The response is from network in browser and i already checked the model using ember plugin on browser that team data doesn't load. How can i use the same api call to load multiple models.
a few things to notice:
dont put the id in attributes
dont name an attribute type. Really dont! It's a reserved keyword.
relationships are not attributes and should be under relationships
use the included array to sideload data
ids must be strings
so for example this would be a valid payload:
{
"meta": {
"type": "match"
},
"data": [
{
"id": "1119536",
"type": "team",
"attributes": {
"match-type": "match"
},
"relationships": {
"team": {
"data": {
"type": "team",
"id": "1"
}
},
"opponent": {
"data": {
"type": "team",
"id": "3"
}
}
}
}
],
"included": [
{
"type": "team",
"id": "1",
"attributes": {
"name": "England",
"logo": null
}
},
{
"type": "team",
"id": "3",
"attributes": {
"name": "Pakistan",
"logo": null
}
}
]
}

custom mapping for mapper attachment type with elasticsearch-persistence ruby

In my project I store data in active record model and index html document in elasticsearch using mapper-attachments plugin. My document mapping look like this:
include Elasticsearch::Model
settings index: { number_of_shards: 5 } do
mappings do
indexes :alerted
indexes :title, analyzer: 'english', index_options: 'offsets'
indexes :summary, analyzer: 'english', index_options: 'offsets'
indexes :content, type: 'attachment', fields: {
author: { index: "no"},
date: { index: "no"},
content: { store: "yes",
type: "string",
term_vector: "with_positions_offsets"
}
}
end
end
I run a query to double check my doc mapping and the result:
"mappings": {
"feed_entry": {
"properties": {
"content": {
"type": "attachment",
"path": "full",
"fields": {
"content": {
"type": "string",
"store": true,
"term_vector": "with_positions_offsets"
},
It works great (the type: 'attachment' above). I can do the search through html doc perfectly.
I have a performance problem with activerecord which is mysql and I don't really need to store it in database so I decide to migrate to store in elasticsearch.
I am doing an experiment with elasticsearch-persistence gem.
I configure the mapping as below:
include Elasticsearch::Persistence::Model
attribute :alert_id, Integer
attribute :title, String, mapping: { analyzer: 'english' }
attribute :url, String, mapping: { analyzer: 'english' }
attribute :summary, String, mapping: { analyzer: 'english' }
attribute :alerted, Boolean, default: false, mapping: { analyzer: 'english' }
attribute :fingerprint, String, mapping: { analyzer: 'english' }
attribute :feed_id, Integer
attribute :keywords
attribute :content, nil, mapping: { type: 'attachment', fields: {
author: { index: "no"},
date: { index: "no"},
content: { store: "yes",
type: "string",
term_vector: "with_positions_offsets"
}
}
but when i do a query to mapping i got something like this:
"mappings": {
"entry": {
"properties": {
"content": {
"properties": {
"_content": {
"type": "string"
},
"_content_type": {
"type": "string"
},
"_detect_language": {
"type": "boolean"
},
which is wrong. can anyone tell me how to do a mapping with attachment type ?
Really appreciate your help.
In the mean time, I have to hard-code it this way:
def self.recreate_index!
mappings = {}
mappings[FeedEntry::ELASTIC_TYPE_NAME]= {
"properties": {
"alerted": {
"type": "boolean"
},
"title": {
#for exact match
"index": "not_analyzed",
"type": "string"
},
"url": {
"index": "not_analyzed",
"type": "string"
},
"summary": {
"analyzer": "english",
"index_options": "offsets",
"type": "string"
},
"content": {
"type": "attachment",
"fields": {
"author": {
"index": "no"
},
"date": {
"index": "no"
},
"content": {
"store": "yes",
"type": "string",
"term_vector": "with_positions_offsets"
}
}
}
}
}
options = {
index: FeedEntry::ELASTIC_INDEX_NAME,
}
self.gateway.client.indices.delete(options) rescue nil
self.gateway.client.indices.create(options.merge( body: { mappings: mappings}))
end
And then override the to_hash method
def to_hash(options={})
hash = self.as_json
map_attachment(hash) if !self.alerted
hash
end
# encode the content to Base64 formatj
def map_attachment(hash)
hash["content"] = {
"_detect_language": false,
"_language": "en",
"_indexed_chars": -1 ,
"_content_type": "text/html",
"_content": Base64.encode64(self.content)
}
hash
end
Then I have to call
FeedEntry.recreate_index!
before hand to create the mapping for elastic search. Becareful when you update the document you might end up with double base64 encoding of the content field. In my scenario, I checked the alerted field.

how to get specific field in an included object

I'm trying to get a specific field in a api call from an included object.
I get an empty array.
filter =
{"where":{"type":"person"}, "include":["objectA"], "fields":"objectA.name"}
What am i doing wrong?
If for example you have the following data model:
Model: Customer.
Fields: id, name.
Model: Order.
Fields: id, date, description, customerId.
Order.belongsTo(Customer, {foreignKey: ‘customerId’});
You can get only the Customer name by writing this filter:
var filter = {
"where": {
"id": 1
},
"include": [
{
"relation": "customer",
"scope": {
"fields": [
"name"
]
}
}
]
}
Order.find(filter, function(err, order) {
...
})
and in your case i'm guessing the filter suppose to be something like this:
{
"where": {
"type": "person"
},
"include": {
"relation": "objectA",
"scope": {
"fields": ["objectA.name"]
}
}
}

Store not getting loaded

I am using Sencha V2. I am trying to populate my list with the values in a Users.json file.
The code in my list file is
Ext.define('iPolis.view.personlist',{
extend:'Ext.List',
xtype: 'personlist',
requires: [
'Ext.List',
'Ext.form.FieldSet',
'Ext.Button'
],
config: {
fullscreen:true,
items: [
{
xtype:'toolbar',
docked:'top',
title:'iPolis',
items:[
{
ui:'back',
icon:'home',
iconCls:'home',
iconMask:true,
id: 'homebtn',
handler:function ()
{
}
},
]
},
{
xtype : 'list',
store : 'personListStore',
itemTpl : '<div class="contact">HI <br/> {name}</div>'
}
}
]
}
});
and the store is calling the file using:
Ext.define('iPolis.store.personListStore', {
extend: 'Ext.data.Store',
storeId:'personListStore',
model : 'iPolis.model.personListModel',
proxy : {
type : 'ajax',
url : '/users.json',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true
});
The code for my json file is:
{
"users": [
{
"id": 1,
"name": "Ed Spencer",
"email": "ed#sencha.com"
},
{
"id": 2,
"name": "Abe Elias",
"email": "abe#sencha.com"
}
]
}
I am gettin a blank screen. I have tried everything but no data is displayed on the screen.
In your list the store is personStore but your trying to use personListStore.
Ext.define('iPolis.store.personListStore', {
extend: 'Ext.data.Store',
config:{
model : 'iPolis.model.personListModel',
storeId:'personListStore',
proxy : {
type : 'ajax',
url : '/users.json',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true } });
try this may help you.