Get Ember Data working with array of objects - rest

I have a simple Ember Data app to list and show various objects.
My /servers.json API (for example) return this kind of format:
[
{
"hosted_domain" : "example.com",
"status" : 1,
"name" : "srv0443",
"id" : 443
},
{
"id" : 392,
"status" : 1,
"name" : "srv0392",
"hosted_domain" : "example.com"
},
{
"hosted_domain" : "example.com",
"id" : 419,
"name" : "srv0419",
"status" : 1
}
]
But I got the following error:
Assertion Failed: The response from a findAll must be an Array, not undefined
Ember Data expects this kind of format:
{
"servers" : [
{
"name" : "srv0443",
"status" : 1,
"id" : 443,
"hosted_domain" : "example.com"
},
{
"status" : 1,
"name" : "srv0392",
"id" : 392,
"hosted_domain" : "example.com"
},
{
"status" : 1,
"name" : "srv0419",
"hosted_domain" : "example.com",
"id" : 419
},
]
}
I know I can override the payload with the extractArray of the RESTSerializer.
It's works by doing payload = { servers: payload } but how get it working in a generic way?
How can I do to catch the needed key of an model type?
In a more general way, what is the good REST format, by convention?
Thanks.

Ember Data works by having the data follow a certain convention ({servers: payload}). So the data either needs to conform, or you have to extend the serializer as you mentioned (or some other customization like overriding the model's findAll() method). There isn't anyway around it, if you want to use Ember Data. Of course, you don't have to use Ember Data. Here is a good article about not using it: http://eviltrout.com/2013/03/23/ember-without-data.html
To customize the serializer you can extend it like this:
App.ServerSerializer = DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
this._super(store, type, {servers: payload});
},
});
Extract array is automatically called by ember after it gets a response from the server. This will put in the format ember data expects, then pass it on to continue processing as usual. But you will have to do that for each type of model. If you override App.ApplicationSerializer instead you might be able to use the type paramter to figure out which key should go in the modified payload, so it will work for any model, but I can't check it right now.

Finally found a solution by using primaryType.typeKey and ember-inflector tool on the RESTSerializer:
App.ApplicationSerializer = DS.RESTSerializer.extend
extractArray: (store, primaryType, payload) ->
# Payload reload with { type.pluralize: hash }
payloadKey = Ember.Inflector.inflector.pluralize primaryType.typeKey
payloadReloaded = []
payloadReloaded[payloadKey] = payload
#_super store, primaryType, payloadReloaded
In a nutshell:
Get the type key (e.g. server)
Pluralize it (e.g. servers)
Add it as payload master key (e.g. { servers: payload }
And that's it!
Please feel free to comment this solution if you have a better proposition.

Related

What is the best way of writing a collection schema to map another collection?

In mongodb I have many collection like below
boys_fashion
girls_fashion
gents_fashion
ladies_fashion
girls_accessories
gents_accessories
ladies_accessories
based on some fields I need to use different collection. So for that I thought to create a collection which will map to a specific collection. Below is the collection I have created for that.
{
"type" : "Fashion",
"gender" : "Gents",
"kids" : "true",
"collection" : "boys_fashion"
}
{
"type" : "Accessories",
"gender" : "Ladies",
"kids" : "false",
"collection" : "ladies_accessories"
}
{
"type" : "Fashion",
"gender" : "Gents",
"kids" : "false",
"collection" : "gents_fashion"
}
{
"type" : "Accessories",
"gender" : "Ladies",
"kids" : "true",
"collection" : "girls_accessories"
}
{
"type" : "Accessories",
"gender" : "Gents",
"kids" : "true",
"collection" : "gents_accessories"
}
{
"type" : "Accessories",
"gender" : "Gents",
"kids" : "false",
"collection" : "gents_accessories"
}
Is this is the right way to do this? or please suggest me some other ways
If I stored like below(the above option is similar to RDBMS. Since its mongo I guess I used this way). How can I write a query for fetching the collection?
{
"fashion" : {
"gents" : {
"true" : "boys_fashion",
"false" : "gents_fashion"
}
},
"accessories" : {
"ladies" : {
"true" : "girls_accessories",
"false" : "ladies_accessories"
}
}
}
Assumptions:
There were one collection before and you split them into multiple collections as they are getting large and you want to solve it without sharing.
I would not even create a collection for the following data. This data is static reference data and will act as a router. On start up, application loads this data and creates a router.
{
"type" : "Fashion",
"gender" : "Gents",
"kids" : "true",
"collection" : "boys_fashion"
}
{
"type" : "Accessories",
"gender" : "Ladies",
"kids" : "false",
"collection" : "ladies_accessories"
}
...
What do I mean by creating a kind of router by that static configuration file? Lets say you receive a query fashionable items for baby girls. This router will tell you, hey you need to search girls_accessories collection. And you send the query to girls_accessories collection and return the result.
Lets take another example, you receive a query for fashionable items for female. This router will tell you hey you need to search, ladies_accessories and girls_accessories. You send the query to both collections and combine the result and send it back.
Conclusion
If my assumptions are correct, you don't need a collection to store the reference data. What you have done is manual sharding by splitting the data across different collections, the reference data will act as router to tell you where to search and combine
Update based on comments
Query does not involve multiple collections
Administrator can add new collection and application should query it without modifying code.
Solution 1
You create a collection for the reference data too
Downside to this is that every query involves two calls to database. First to fetch the static data and second to the data collection
Solution 2
You create a collection for the reference data too
You also build a Dao on top of that which uses #Cacheable for the method that return this data.
You also add a method in Dao to clear the cache with #CacheEvict and have a rest endpoint like /refresh-static-data that will call this method`
Downside to this method is that whenever administrator add new collection, you need to call the endpoint.
Solution 3
Same as solution 2 but instead of having an endpoint to clear the cache, you combine it with scheduler
#Scheduled(fixedRate = ONE_DAY)
#CacheEvict(value = { CACHE_NAME })
public void clearCache() {
}
Downside to this solution is that you have to come up with a period for fixedRate which is acceptable to your business
I added the collection like below.
/* 1 */
{
"type" : "fashion",
"category" : [
{
"value" : "gents",
"true" : "boys_fashion",
"false" : "gents_fasion"
}
]
}
/* 2 */
{
"type" : "accessories",
"category" : [
{
"value" : "ladies",
"true" : "girls_accessories",
"false" : "ladies_accessories"
}
]
}
and will fetch the data using the below query
findOne({"type":type,"category.value":cvalue},{"_id": 0, "category": {"$elemMatch": {"value": cvalue}}})
Try to make subdocuments in MongoDB, instead of nested objects
https://mongoosejs.com/docs/subdocs.html

JFrog Artifactory API query for object properties does not return requested detail

I am requesting label properties for docker artifact, perhaps the url is not correct? I get response object (json) but label properties are not included. Code example:
response = Net::HTTP.get_with_headers("http://myrepo:8081/artifactory/api/storage/dockerv2-local/anonymizer/functional/manifest.json;docker.label.com.company.info.build='*'",
{'Authorization' => 'Bearer <REDACTED>'})
if response.code.to_s == "200"
puts ("Artifactory response "+ response.body)
puts ("response object: "+response.inspect())
else
puts ("Artifactory request returned "+response.code.to_s)
end
Connecting to artifactory
Artifactory response {
"repo" : "dockerv2-local",
"path" : "/anonymizer/functional/manifest.json",
"created" : "2018-03-14T14:52:22.681-07:00",
"createdBy" : "build",
"lastModified" : "2018-03-15T15:52:34.225-07:00",
"modifiedBy" : "build",
"lastUpdated" : "2018-03-15T15:52:34.225-07:00",
"downloadUri" : "http://myrepo:8081/artifactory/dockerv2-local/anonymizer/functional/manifest.json",
"mimeType" : "application/json",
"size" : "1580",
"checksums" : {
"sha1" : "bf2a1f85c7ab8cec14b64d172b7fdaf420804fcb",
"md5" : "9c1bbfc77e2f44d96255f7c1f99d2e8d",
"sha256" : "53e56b21197c57d8ea9838df7cffb3d8f33cd714998d620efd8a34ba5a7e33c0"
},
"originalChecksums" : {
"sha256" : "53e56b21197c57d8ea9838df7cffb3d8f33cd714998d620efd8a34ba5a7e33c0"
},
"uri" : "http://myrepo:8081/artifactory/api/storage/dockerv2-local/anonymizer/functional/manifest.json"
}
response object: #<Net::HTTPOK 200 OK readbody=true>
If I understand you correctly, you want to get the properties of the manifest.json file, "docker.label.com.company.info.build" in particular.
From looking at your command:
response = Net::HTTP.get_with_headers("http://myrepo:8081/artifactory/api/storage/dockerv2-local/anonymizer/functional/manifest.json;docker.label.com.company.info.build='*'",
It seems that you are using a semicolon to get the properties, which is not the right way. As you can see in this REST API, in order to use the get properties you should use the ampersand sign, so your command should look like:
response = Net::HTTP.get_with_headers("http://myrepo:8081/artifactory/api/storage/dockerv2-local/anonymizer/functional/manifest.json&docker.label.com.company.info.build='*'",

ALPS sample implementation

I'm looking for a sample client implementation utilizing ALPS (not the mountains but the Application-Level Profile Semantics).
Do YOU! have one?
I've looked into the related RFC draft and discussions but still can figure it quite out.
Specifically I would like to know how my client should know what the descriptor describes, given that my client supposedly knows nothing about the structure and semantics of the REST API as the REST principle demands?
As a human I know that a descriptor with an id tag called "users" is likely to describe how to interact with users but how is my client to know without me telling him explicitly?
I know I could insert some kind of keyword to show up in the descriptor and tell my client to match the appropriate ones but this seems hardly the right way.
I happily provide a more detailed example given somebody is willing to read it.
I'm exploring ALPS for the first time too, and my understanding from that RFC draft wasn't immediate either.
Here is a slideshow (166 slides, so it's not possible to copy it all into this answer) from the author of the RFC which I think gives a much better understanding of the role ALPS plays.
As a human I know that a descriptor with an id tag called users is likely to describe how to interact with users but how is my client to know this without me telling him explicitly?
From this slideshow, I deduce this answer to your question: He doesn't.
In the slideshow, a sample ALPS profile is compared with equivalent HTML code for a form submit. The browser knows how to render the HTML to the screen, but only the human knows what it means to POST that form with those input fields, using that submit button.
Here is an example Complete JSON Representation taken from alps.io
{
"alps" : {
"version" : "1.0",
"doc" : {
"href" : "http://example.org/samples/full/doc.html"
},
"descriptor" : [
{
"id" : "search",
"type" : "safe",
"doc" : {"value" :
"A search form with a two inputs"
},
"descriptor" : [
{
"id" : "value",
"name" : "search",
"type" : "descriptor",
"doc" : { "value" : "input for search" }
},
{ "href" : "#resultType" }
]
},
{
"id" : "resultType",
"type" : "descriptor",
"description" : {"value" : "results format"},
"ext" : [
{
"href" : "http://alps.io/ext/range",
"value" : "summary,detail"
}
]
}
]
}
}
Take, for example, a generic mobile phone app which is displaying screens to the user based on REST responses. Say a HAL+Json response contains a reference to a search entity. The app can lookup in this ALPS document what a search entity is, and can be coded on how to represent that. Namely, a search is something which has a name/value pair (with an id) and a href. The href refers to the second descriptor with id resultType which lets the app know the format to expect for search results. The actual URLs and data involved would come from the REST responses.
From July 2014, here is a Spring blog article describing the ALPS for an app which manages a "To Do List". The ALPS document describes
What is a todo entity
What actions can be done with a todo entity
An abridged version of the ALPS profile for that small app:
{
"version" : "1.0",
"descriptors" : [ {
"id" : "todo-representation",
"descriptors" : [ {
"name" : "description",
"doc" : {
"value" : "Details about the TODO item",
"format" : "TEXT"
},
"type" : "SEMANTIC"
}, {
"name" : "title",
"doc" : {
"value" : "Title for the TODO item",
"format" : "TEXT"
},
"type" : "SEMANTIC"
}, {
"name" : "id",
"type" : "SEMANTIC"
}, {
"name" : "completed",
"doc" : {
"value" : "Is it completed?",
"format" : "TEXT"
},
"type" : "SEMANTIC"
} ]
}, {
"id" : "create-todos",
"name" : "todos",
"type" : "UNSAFE",
"rt" : "#todo-representation"
}, {
"id" : "get-todos",
"name" : "todos",
"type" : "SAFE",
"rt" : "#todo-representation"
}, {
"id" : "delete-todo",
"name" : "todo",
"type" : "IDEMPOTENT",
"rt" : "#todo-representation"
} ]
}
I guess one way to think of it might as a kind "schema", but instead of database tables, it's describing the scope of REST responses.

Neo4j - Reduce number of fields returned in REST response

The response returned by Neo4j's REST interfaces are very verbose - they return not only the data in each node requested, but also the full discoverability for every node requested. If I just want some node data, the results are about 20 times bigger than I actually need, and I run into problems with out-of-memory exceptions and the like.
For example, a request for a node might return the following:
{
"labels" : "http://giuncwy02:7475/db/data/node/67/labels",
"outgoing_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/out",
"data" : {
"id" : "908754897618956",
"currentStatus" : "Active",
},
"traverse" : "http://giuncwy02:7475/db/data/node/67/traverse/{returnType}",
"all_typed_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/all/{-list|&|types}",
"self" : "http://giuncwy02:7475/db/data/node/67",
"property" : "http://giuncwy02:7475/db/data/node/67/properties/{key}",
"outgoing_typed_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/out/{-list|&|types}",
"properties" : "http://giuncwy02:7475/db/data/node/67/properties",
"incoming_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/in",
"extensions" : { },
"create_relationship" : "http://giuncwy02:7475/db/data/node/67/relationships",
"paged_traverse" : "http://giuncwy02:7475/db/data/node/67/paged/traverse/{returnType}{?pageSize,leaseTime}",
"all_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/all",
"incoming_typed_relationships" : "http://giuncwy02:7475/db/data/node/67/relationships/in/{-list|&|types}",
"metadata" : {
"id" : 67,
"labels" : [ "Substation" ]
}
}
Is there a way to reduce the amount of information returned in the response? All I really want for each node is this:
{
"id" : "908754897618956",
"currentStatus" : "Active",
}
or even:
[ "908754897618956", "Active" ]
Is that achievable? When I'm requesting hundreds of thousands of nodes it makes quite a big difference.
There is no config option to tweak this for the existing db/data/node REST endpoint.
As Christophe sketched you can use the transactional endpoint and a tailored Cypher statement to return those properties you want to see.
The other option is writing your own unmanaged extension to the Neo4j server that returns the nodes as you've specified.
The lowest hanging fruit is for sure the first approach.

Get key from map and retrieve data with it in one query

I have two collections in mongo. First, is the actual data:
{ "_id" : "internal_key1", "data" : "some data1" }
{ "_id" : "internal_key2", "data" : "some data2" }
{ "_id" : "internal_key3", "data" : "some data3" }
Another one is a map of keys as provided by some external service to my internal keys:
{ "_id" : "ext_key111", "internal" : "internal_key1" }
{ "_id" : "ext_key222", "internal" : "internal_key2" }
{ "_id" : "ext_key333", "internal" : "internal_key3" }
If I only have external key, can I somehow retrieve data (for example, given "ext_key111" retrieve "some data1") with just one query? Not counting eval-like stuff, of course.
The short answer is NO.
You are basically asking for a join and MongoDB explicitly does not allow joins.
However, depending on your requirements, you may be able to get away with the following structure:
{ "_id" : "internal_key1", "ext_id": "ext_key111", "data" : "some data1" }
You can create an additional index on ext_id. You can even make it unique which seems to match your data.
db.ensureIndex({ ext_id: 1 }, { unique: true, background: true })