From 09280a81eb4f7529df5b64d858d756f36de92b16 Mon Sep 17 00:00:00 2001 From: pangratz Date: Tue, 12 Jul 2016 08:38:49 +0200 Subject: [PATCH 01/10] ember-data: links and meta improvements --- ...0000-ember-data-links-meta-improvements.md | 340 ++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 text/0000-ember-data-links-meta-improvements.md diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md new file mode 100644 index 0000000000..6907e04149 --- /dev/null +++ b/text/0000-ember-data-links-meta-improvements.md @@ -0,0 +1,340 @@ +- Start Date: 2016-07-29 +- RFC PR: (leave this empty) +- Ember Issue: (leave this empty) + +# Summary + +Ember Data uses JSON-API internally: this RFC aims to provide a public API for +accessing and interacting with the already available data. + +Meta and links are made available via references for record, belongs-to, +has-many and record-arrays (using the new `RecordArrayReference`). All those +references will expose their corresponding meta and links via – surprise – +`meta()` and `links()` methods. Since meta is only a plain JavaScript object, +there is no need for further abstraction. A link on the other hand is +represented as a `LinkReference`, which allows to get the related meta and to +load the link via `load()`. + +Since references are not observable by design, hooks are proposed in the +accompanying [RFC #123](https://github.com/emberjs/rfcs/pull/123) which allow +to retrieve data from the reference, derive some properties and set it on the +relevant model instances. + +The references API is intentionally designed to be a low level API. It can be +used in a higher level API's provided by add-ons; or even in ember-data itself, +once a use case is flushed out and it's reasonable to being in core. + +#### In short + +- Add `LinkReference` which is an abstraction for a single link and its + properties (meta, name, href) and action (load) +- Add a `RecordArrayReference` which abstracts a collection of records for its + properties (meta, links) and action (reload) +- A new `links()` method is added to record, belongs-to, has-many and + record-array references which returns all associated links +- Hooks which allow to react to changes when the backing data of references + changes (hooks might not be part of this RFC, but are already somewhat + addressed in [RFC#123](https://github.com/emberjs/rfcs/pull/123)) + +# Motivation + +## Status quo + +Currently meta is only available on relationships via the `belongs-to` and +`has-many` references. Record level meta data is not available as `meta` on the +`DS.Model` instance, because the source for record level meta data can come +from various places: `store.queryRecord`, `store.findRecord` or even via +`store.push` and `store.pushPayload`. A problem with setting `meta` +automatically as property on the `DS.Model` instance might overwrite previously +set meta of a different call. + +Meta is available on the `ManyArray` of a has many relationship. It is also set +on the `AdapterPopulatedRecordArray`s of `store.query`. It is however currently +not possible to get the `meta` for a `store.findAll`. + +In terms of links currently only `related` is handled on relationships. Though +the [`ds-links-in-record-array` +feature](https://github.com/emberjs/data/pull/4263) makes links available on +the `AdapterPopulatedRecordArray` of a `store.query`, Ember Data doesn't offer +a dedicated API to load the `next` link for example to support pagination. +Also, according to JSON-API specification, links can have `meta` too, which is +also not supported in core Ember Data. + +## Proposal + +- allow to interact with links for single resource and resource collections +- make references consistent, so they are available for record arrays and links +- provide low level API for internally used JSON-API +- provide hooks to react to changes in references + ([RFC#123](https://github.com/emberjs/rfcs/pull/123)) + +# Detailed design + +### `DS.LinkReference` + +This new reference is added and represents the reference to a single link entry +of the JSON-API `links: {}` object: + +```js +/** + Reference to a single link of a JSON-API links object. +*/ +class LinkReference { + + /** + Get the name of the link. + + E.g. "related", "self" or "next" + + @return {String} name of the link + */ + name() + + /** + Get the href of the link. + + @return {String} href + */ + href() + + /** + Get the meta associated with the link. + + @return {Object} meta + */ + meta() + + /** + Load the link. + + Returned promise resolves with a `DS.RecordArray`. + + @return {Promise} + */ + load() + + /** + Get the reference to which this link is connected to. + + This can either be a `RecordReference`, `BelongsToReference`, + `HasManyReference` or `RecordArrayReference`. + + @return {DS.Reference} reference to which this link + is connected to + */ + parentRef() + +} +``` + +### `DS.RecordArrayReference` + +This new type references an array of records, returned for example by +`store.query` or `store.findAll`. + +```js +class RecordArrayReference { + + /** + Get the `DS.RecordArray` which holds all the `DS.Model`s. + + This can either be the result for a `store.query` or a + `store.findAll`. + + @return {DS.RecordArray} + */ + value() + + /** + Reload the underlying `RecordArray`. + + This will re-fetch the `self` link. + + @return {Promsise} resolving with the reloaded `DS.RecordArray` + */ + reload() + + /** + Get all links associated with this `DS.RecordArray`. + + This is useful for pagination for example. + + @return {Array} + */ + links() + + /** + Get the meta associated with the underlying `DS.RecordArray` + + @return {Object} + */ + meta() + +} +``` + +### `RecordReference.meta()` + +The `RecordReference` gets a new `meta()` method, which returns the last +associated meta for the record pushed into the store via `findRecord()`, +`queryRecord()`, `store.push()` or `store.pushPayload()`. + +```js +// GET /books/1 +// { +// data: { … }, +// meta: { +// isThriller: true +// } +// } +store.findRecord("book", 1).then(function(book) { + // get the RecordReference + let bookRef = book.ref(); + + let meta = bookRef.meta(); + assert.equal(meta.isThriller, true); +}); +``` + +### `[Record|BelongsTo|HasMany|RecordArray]Reference.links()` + +The `links()` method on those references allows to get all link references +associated with the reference, or the specific `LinkReference`, if the name of +the link is passed: + +```js +// { +// data: { +// type: "book", +// id: 1, +// relationships: { +// chapters: { +// data: [ … ], +// links: { +// "next": { … }, +// "prev": { … } +// } +// } +// } +// } +// } +let chaptersRef = store.peekRecord("book", 1).hasMany("chapters"); + +// [, ] +let links = chaptersRef.links(); + +// DS.LinkReference +let nextLink = chaptersRef.links("next"); +``` + +### Adaptions to current references / models + +To fully complete the circle of references, the existing classes are modified +as follows: + +- `RecordReference` + - add `links()` +- `Model` + - add `ref()` to get the corresponding `RecordReference` +- `BelongsToReference` + - add `links()` + - add `parentRef()` to get a reference to the parent `RecordReference` +- `HasManyReference` + - add `links()` + - add `parentRef()` to get a reference to the parent `RecordReference` +- `ManyArray` + - add `ref()` to get the corresponding `HasManyReference` +- `AdapterPopulatedRecordArray` and `RecordArray` + - add `ref()` to get the corresponding `RecordArrayReference` + +### Hooks + +Additionally to the new types of references and the extension of the existing +ones, the Model Lifecycle Hooks proposed in +[RFC#123](https://github.com/emberjs/rfcs/pull/123) allow to react to changes +for the underlying data of references. The hooks are explained in greater +detail in that RFC, but record level meta data handling for example might look +like this: + +```js +// app/models/books.js +import Model from "ember-data/model"; + +const Book = Model.extend({}); + +Book.reopenClass({ + + didReceiveData(recordRef) { + let book = recordRef.value(); + let meta = recordRef.meta(); + + if (book && meta) { + // set meta on the DS.Model instance so it can be + // accessed in the template + book.set("meta", meta); + } + } + +}); + +export default Book; +``` + +## Sample code + +### Pagination + +```js +let allPages = Ember.A(); + +// GET /books?page=1 +store.query("book", { page: 1 }).then(function(books) { + allPages.addObjects(books.toArray()); + + let booksRef = books.ref(); + let nextPage = booksRef.links("next"); + + // GET /books?page=2 + nextPage.load().then(function(nextBooks) { + allPages.addObjects(nextBooks.toArray()); + }); +}); +``` + +### Meta for `findAll` + +```js +// GET /books +store.findAll("book").then(function(books) { + // DS.RecordArrayReference + let booksRef = books.ref(); + + let meta = booksRef.meta(); +}); +``` + +# How we teach this + +- for the beginning not in the guides, until this is fleshed out and seems + stable / usable +- in the beginning only API docs within the source + +# Drawbacks + +- massive increase of low level, public API +- though it extends the concept of references, it's a significant increase of + API surface + +# Alternatives + +- N/A + +# Unresolved questions + +- is `DS.LinkReference` actually a `DS.Link` and not really a reference? +- rename `name()` of `LinkReference`? What is the official terminology for it? +- what type of `RecordArray` does `LinkReference#load()` resolve with? Do we + need a new type or is `DS.AdapterPopulatedRecordArray` sufficient? +- should `books.hasMany("chapters").links("next").load()` update the content of + the relationship? Should there be a dedicated API for this, e.g. + `books.hasMany("chapters").loadLink("next")`? From cebe6d71f8d4d3e8245619aa95dd2b10020c972c Mon Sep 17 00:00:00 2001 From: pangratz Date: Mon, 1 Aug 2016 18:32:23 +0200 Subject: [PATCH 02/10] Add more adapter methods as possible sources for single record meta --- text/0000-ember-data-links-meta-improvements.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index 6907e04149..464873473c 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -177,7 +177,8 @@ class RecordArrayReference { The `RecordReference` gets a new `meta()` method, which returns the last associated meta for the record pushed into the store via `findRecord()`, -`queryRecord()`, `store.push()` or `store.pushPayload()`. +`queryRecord()`, `store.push()` or `store.pushPayload()` and `createRecord()` +and `updateRecord()`. ```js // GET /books/1 From f490739e26578b5c07c4c75582be3bdf53eea292 Mon Sep 17 00:00:00 2001 From: pangratz Date: Sat, 13 Aug 2016 21:55:42 +0200 Subject: [PATCH 03/10] Adjustments afer giving this more thought - replace RecordArrayReference in favor of DocumentReference - outline different locations of meta - add code sample for different meta of a resource - add unresolved question for additional `extractMeta` hook on `RESTSerializer` --- ...0000-ember-data-links-meta-improvements.md | 251 ++++++++++++++---- 1 file changed, 202 insertions(+), 49 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index 464873473c..ab7d352a5a 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -8,7 +8,7 @@ Ember Data uses JSON-API internally: this RFC aims to provide a public API for accessing and interacting with the already available data. Meta and links are made available via references for record, belongs-to, -has-many and record-arrays (using the new `RecordArrayReference`). All those +has-many and JSON-API documents (using the new `DocumentReference`). All those references will expose their corresponding meta and links via – surprise – `meta()` and `links()` methods. Since meta is only a plain JavaScript object, there is no need for further abstraction. A link on the other hand is @@ -28,8 +28,8 @@ once a use case is flushed out and it's reasonable to being in core. - Add `LinkReference` which is an abstraction for a single link and its properties (meta, name, href) and action (load) -- Add a `RecordArrayReference` which abstracts a collection of records for its - properties (meta, links) and action (reload) +- Add a `DocumentReference` which describes a JSON-API document and its + properties (meta, links, data) - A new `links()` method is added to record, belongs-to, has-many and record-array references which returns all associated links - Hooks which allow to react to changes when the backing data of references @@ -40,6 +40,18 @@ once a use case is flushed out and it's reasonable to being in core. ## Status quo +### Links + +In terms of links currently only `related` is handled on relationships. Though +the [`ds-links-in-record-array` +feature](https://github.com/emberjs/data/pull/4263) makes links available on +the `AdapterPopulatedRecordArray` of a `store.query`, Ember Data doesn't offer +a dedicated API to load the `next` link for example to support pagination. +Also, according to JSON-API specification, links can have `meta` too, which is +also not supported in core Ember Data. + +### Meta + Currently meta is only available on relationships via the `belongs-to` and `has-many` references. Record level meta data is not available as `meta` on the `DS.Model` instance, because the source for record level meta data can come @@ -52,18 +64,103 @@ Meta is available on the `ManyArray` of a has many relationship. It is also set on the `AdapterPopulatedRecordArray`s of `store.query`. It is however currently not possible to get the `meta` for a `store.findAll`. -In terms of links currently only `related` is handled on relationships. Though -the [`ds-links-in-record-array` -feature](https://github.com/emberjs/data/pull/4263) makes links available on -the `AdapterPopulatedRecordArray` of a `store.query`, Ember Data doesn't offer -a dedicated API to load the `next` link for example to support pagination. -Also, according to JSON-API specification, links can have `meta` too, which is -also not supported in core Ember Data. +The JSON-API specification states that meta information can be present in +various locations on a JSON-API document. + +#### Single resource + +```js +{ + data: { + id: 1, + type: "book", + relationships: { + author: { + data: { … }, + + // relationship level meta + meta: { + lastUpdatedAt: "2016-08-13T12:34:56Z" + } + } + }, + + // resource level meta + meta: { + lastUpdatedAt: "2016-08-13T12:34:56Z" + }, + + links: { + self: { + href: "…", + + // link level meta + meta: { + canBeCached: false + } + } + } + }, + + // document level meta + meta: { + apiRateLimitRemaining: 35 + } +} +``` + +#### Resource collection + +```js +{ + data: [{ + id: 1, + type: "book", + relationships: { + author: { + data: { … }, + + // relationship level meta + meta: { + lastUpdatedAt: "2016-08-13T12:34:56Z" + } + } + }, + + // resource level meta + meta: { + lastUpdatedAt: "2016-08-13T12:34:56Z" + }, + + links: { + self: { + href: "…", + + // link level meta + meta: { + canBeCached: false + } + } + } + }], + + // document level meta + meta: { + total: 123, + apiRateLimitRemaining: 34 + } +} +``` + +While a resource collection has its meta on the document level, a single +resource might have resource specific meta data under `data.meta`, where the +response specific meta data is located in the root level `meta`. Both meta data +need to be accesible for a record. ## Proposal - allow to interact with links for single resource and resource collections -- make references consistent, so they are available for record arrays and links +- make references consistent, so they are available for links and documents - provide low level API for internally used JSON-API - provide hooks to react to changes in references ([RFC#123](https://github.com/emberjs/rfcs/pull/123)) @@ -117,7 +214,7 @@ class LinkReference { Get the reference to which this link is connected to. This can either be a `RecordReference`, `BelongsToReference`, - `HasManyReference` or `RecordArrayReference`. + `HasManyReference` or `DocumentReference`. @return {DS.Reference} reference to which this link is connected to @@ -127,65 +224,55 @@ class LinkReference { } ``` -### `DS.RecordArrayReference` +### `DS.DocumentReference` -This new type references an array of records, returned for example by -`store.query` or `store.findAll`. +This new type represents a JSON-API document: ```js -class RecordArrayReference { - - /** - Get the `DS.RecordArray` which holds all the `DS.Model`s. - - This can either be the result for a `store.query` or a - `store.findAll`. - - @return {DS.RecordArray} - */ - value() - - /** - Reload the underlying `RecordArray`. - - This will re-fetch the `self` link. - - @return {Promsise} resolving with the reloaded `DS.RecordArray` - */ - reload() +class DocumentReference { /** - Get all links associated with this `DS.RecordArray`. + Get all document level links. - This is useful for pagination for example. + This is useful for pagination of resource collections for example. @return {Array} */ links() /** - Get the meta associated with the underlying `DS.RecordArray` + Get the document level meta. @return {Object} */ meta() + /** + Get the references, associated with the data for the document. + + @return {DS.RecordReference|Array} + */ + data() + } ``` ### `RecordReference.meta()` -The `RecordReference` gets a new `meta()` method, which returns the last -associated meta for the record pushed into the store via `findRecord()`, -`queryRecord()`, `store.push()` or `store.pushPayload()` and `createRecord()` -and `updateRecord()`. +The `RecordReference` gets a new `meta()` method, which returns the *last +associated record level meta* for the record pushed into the store: ```js // GET /books/1 // { -// data: { … }, -// meta: { -// isThriller: true +// data: { +// id: 1, +// type: "book", +// attributes: { … }, +// relationships: { … }, +// meta: { +// lastUpdatedAt: "2016-08-13T12:34:56Z" +// } // } // } store.findRecord("book", 1).then(function(book) { @@ -193,11 +280,29 @@ store.findRecord("book", 1).then(function(book) { let bookRef = book.ref(); let meta = bookRef.meta(); - assert.equal(meta.isThriller, true); + assert.ok(meta.lastUpdatedAt); +}); + +// GET /books +// { +// data: [{ +// id: 1, +// type: "book", +// meta: { +// isPublished: true +// } +// }] +// } +store.query("book", {}).then(function(books) { + // get the RecordReference of first book in result + let bookRef = books.objectAt(0).ref(); + + let meta = bookRef.meta(); + assert.ok(meta.isPublished); }); ``` -### `[Record|BelongsTo|HasMany|RecordArray]Reference.links()` +### `[Record|BelongsTo|HasMany|Document]Reference.links()` The `links()` method on those references allows to get all link references associated with the reference, or the specific `LinkReference`, if the name of @@ -234,7 +339,9 @@ To fully complete the circle of references, the existing classes are modified as follows: - `RecordReference` + - add `meta()` - add `links()` + - add `documentRef()` - `Model` - add `ref()` to get the corresponding `RecordReference` - `BelongsToReference` @@ -246,7 +353,7 @@ as follows: - `ManyArray` - add `ref()` to get the corresponding `HasManyReference` - `AdapterPopulatedRecordArray` and `RecordArray` - - add `ref()` to get the corresponding `RecordArrayReference` + - add `documentRef()` ### Hooks @@ -289,6 +396,14 @@ export default Book; let allPages = Ember.A(); // GET /books?page=1 +// { +// data: […], +// links: { +// next: { +// href: "…" +// } +// } +// } store.query("book", { page: 1 }).then(function(books) { allPages.addObjects(books.toArray()); @@ -306,11 +421,45 @@ store.query("book", { page: 1 }).then(function(books) { ```js // GET /books +// { +// data: […], +// meta: { +// total: 123 +// } +// } store.findAll("book").then(function(books) { - // DS.RecordArrayReference + // DS.DocumentReference let booksRef = books.ref(); let meta = booksRef.meta(); + assert.ok(meta.total); +}); +``` + +### Meta for record (record and document level) + +```js +// GET /books/1 +// { +// data: { +// id: 1, +// type: "book", +// meta: { +// lastUpdatedAt: "2016-08-13T12:34:56Z" +// } +// }, +// meta: { +// apiRateLimitRemaining: 33 +// } +// } +store.findRecord("book", 1).then(function(book) { + let bookRef = book.ref(); + let bookMeta = bookRef.meta(); + assert.ok(bookMeta.lastUpdatedAt); + + let docRef = bookRef.documentRef(); + let docMeta = docRef.meta(); + assert.ok(docMeta.apiRateLimitRemaining > 0); }); ``` @@ -339,3 +488,7 @@ store.findAll("book").then(function(books) { - should `books.hasMany("chapters").links("next").load()` update the content of the relationship? Should there be a dedicated API for this, e.g. `books.hasMany("chapters").loadLink("next")`? +- since multiple "locations" of meta will be supported, there needs to be a new + hook within the `RESTSerializer` to extract record/document level meta, + additionally to the `extractMeta` hook, which is invoked with the whole + payload From 1b0b9e437ec055a83edb87b30fa1e1b64c8fe5cb Mon Sep 17 00:00:00 2001 From: pangratz Date: Sat, 13 Aug 2016 22:00:26 +0200 Subject: [PATCH 04/10] Restructure "Detailed Design" and group by links and meta --- ...0000-ember-data-links-meta-improvements.md | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index ab7d352a5a..a75d8c633d 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -167,6 +167,8 @@ need to be accesible for a record. # Detailed design +## Links + ### `DS.LinkReference` This new reference is added and represents the reference to a single link entry @@ -224,6 +226,39 @@ class LinkReference { } ``` +### `[Record|BelongsTo|HasMany|Document]Reference.links()` + +The `links()` method on those references allows to get all link references +associated with the reference, or the specific `LinkReference`, if the name of +the link is passed: + +```js +// { +// data: { +// type: "book", +// id: 1, +// relationships: { +// chapters: { +// data: [ … ], +// links: { +// "next": { … }, +// "prev": { … } +// } +// } +// } +// } +// } +let chaptersRef = store.peekRecord("book", 1).hasMany("chapters"); + +// [, ] +let links = chaptersRef.links(); + +// DS.LinkReference +let nextLink = chaptersRef.links("next"); +``` + +## Meta + ### `DS.DocumentReference` This new type represents a JSON-API document: @@ -302,38 +337,7 @@ store.query("book", {}).then(function(books) { }); ``` -### `[Record|BelongsTo|HasMany|Document]Reference.links()` - -The `links()` method on those references allows to get all link references -associated with the reference, or the specific `LinkReference`, if the name of -the link is passed: - -```js -// { -// data: { -// type: "book", -// id: 1, -// relationships: { -// chapters: { -// data: [ … ], -// links: { -// "next": { … }, -// "prev": { … } -// } -// } -// } -// } -// } -let chaptersRef = store.peekRecord("book", 1).hasMany("chapters"); - -// [, ] -let links = chaptersRef.links(); - -// DS.LinkReference -let nextLink = chaptersRef.links("next"); -``` - -### Adaptions to current references / models +## Adaptions to current references / models To fully complete the circle of references, the existing classes are modified as follows: @@ -355,7 +359,7 @@ as follows: - `AdapterPopulatedRecordArray` and `RecordArray` - add `documentRef()` -### Hooks +## Hooks Additionally to the new types of references and the extension of the existing ones, the Model Lifecycle Hooks proposed in From a2c67528e26dede6034f1d4525e0411100f2fc8a Mon Sep 17 00:00:00 2001 From: pangratz Date: Sun, 14 Aug 2016 01:59:15 +0200 Subject: [PATCH 05/10] s/ref()/documentRef()/ for RecordArray's --- text/0000-ember-data-links-meta-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index a75d8c633d..e65b23b72d 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -411,7 +411,7 @@ let allPages = Ember.A(); store.query("book", { page: 1 }).then(function(books) { allPages.addObjects(books.toArray()); - let booksRef = books.ref(); + let booksRef = books.documentRef(); let nextPage = booksRef.links("next"); // GET /books?page=2 @@ -433,7 +433,7 @@ store.query("book", { page: 1 }).then(function(books) { // } store.findAll("book").then(function(books) { // DS.DocumentReference - let booksRef = books.ref(); + let booksRef = books.documentRef(); let meta = booksRef.meta(); assert.ok(meta.total); From 1ef5f0d2c636ef5eff273c3a5bd547f6b1dbd0cb Mon Sep 17 00:00:00 2001 From: IgorT Date: Tue, 18 Oct 2016 18:51:39 +0300 Subject: [PATCH 06/10] simplify --- ...0000-ember-data-links-meta-improvements.md | 77 +++++-------------- 1 file changed, 21 insertions(+), 56 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index e65b23b72d..475e5fcbc7 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -11,9 +11,7 @@ Meta and links are made available via references for record, belongs-to, has-many and JSON-API documents (using the new `DocumentReference`). All those references will expose their corresponding meta and links via – surprise – `meta()` and `links()` methods. Since meta is only a plain JavaScript object, -there is no need for further abstraction. A link on the other hand is -represented as a `LinkReference`, which allows to get the related meta and to -load the link via `load()`. +there is no need for further abstraction. Since references are not observable by design, hooks are proposed in the accompanying [RFC #123](https://github.com/emberjs/rfcs/pull/123) which allow @@ -26,12 +24,12 @@ once a use case is flushed out and it's reasonable to being in core. #### In short -- Add `LinkReference` which is an abstraction for a single link and its - properties (meta, name, href) and action (load) -- Add a `DocumentReference` which describes a JSON-API document and its - properties (meta, links, data) - A new `links()` method is added to record, belongs-to, has-many and record-array references which returns all associated links +- Add `LinkObject` which is an abstraction for a single link and its + properties (meta, href) +- Add a `DocumentReference` which describes a JSON-API document and its + properties (meta, links, data) - Hooks which allow to react to changes when the backing data of references changes (hooks might not be part of this RFC, but are already somewhat addressed in [RFC#123](https://github.com/emberjs/rfcs/pull/123)) @@ -169,25 +167,21 @@ need to be accesible for a record. ## Links -### `DS.LinkReference` +Links are exposed in an object, with each link exposed with the following +properties: + +- `href` +- `meta`: optional meta for the link, or `null` if there is none -This new reference is added and represents the reference to a single link entry -of the JSON-API `links: {}` object: +```js +let links = post.hasMany('authors'); +``` ```js /** - Reference to a single link of a JSON-API links object. + Link */ -class LinkReference { - - /** - Get the name of the link. - - E.g. "related", "self" or "next" - - @return {String} name of the link - */ - name() +{ /** Get the href of the link. @@ -203,34 +197,12 @@ class LinkReference { */ meta() - /** - Load the link. - - Returned promise resolves with a `DS.RecordArray`. - - @return {Promise} - */ - load() - - /** - Get the reference to which this link is connected to. - - This can either be a `RecordReference`, `BelongsToReference`, - `HasManyReference` or `DocumentReference`. - - @return {DS.Reference} reference to which this link - is connected to - */ - parentRef() - } ``` ### `[Record|BelongsTo|HasMany|Document]Reference.links()` -The `links()` method on those references allows to get all link references -associated with the reference, or the specific `LinkReference`, if the name of -the link is passed: +The `links` method on those references allows us to get the links object. ```js // { @@ -250,11 +222,11 @@ the link is passed: // } let chaptersRef = store.peekRecord("book", 1).hasMany("chapters"); -// [, ] +// Object containing links let links = chaptersRef.links(); -// DS.LinkReference -let nextLink = chaptersRef.links("next"); +// LinkObject +let nextLink = chaptersRef.links().next; ``` ## Meta @@ -475,7 +447,7 @@ store.findRecord("book", 1).then(function(book) { # Drawbacks -- massive increase of low level, public API +- increase of low level, public API - though it extends the concept of references, it's a significant increase of API surface @@ -485,14 +457,7 @@ store.findRecord("book", 1).then(function(book) { # Unresolved questions -- is `DS.LinkReference` actually a `DS.Link` and not really a reference? -- rename `name()` of `LinkReference`? What is the official terminology for it? -- what type of `RecordArray` does `LinkReference#load()` resolve with? Do we - need a new type or is `DS.AdapterPopulatedRecordArray` sufficient? -- should `books.hasMany("chapters").links("next").load()` update the content of - the relationship? Should there be a dedicated API for this, e.g. - `books.hasMany("chapters").loadLink("next")`? - since multiple "locations" of meta will be supported, there needs to be a new - hook within the `RESTSerializer` to extract record/document level meta, + hook only within the `RESTSerializer` to extract record/document level meta, additionally to the `extractMeta` hook, which is invoked with the whole payload From c72701b413f9717e6c921291456098aee308ef9e Mon Sep 17 00:00:00 2001 From: pangratz Date: Tue, 18 Oct 2016 20:38:00 +0300 Subject: [PATCH 07/10] Minor cleanup --- ...0000-ember-data-links-meta-improvements.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index 475e5fcbc7..b2c8909329 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -10,8 +10,8 @@ accessing and interacting with the already available data. Meta and links are made available via references for record, belongs-to, has-many and JSON-API documents (using the new `DocumentReference`). All those references will expose their corresponding meta and links via – surprise – -`meta()` and `links()` methods. Since meta is only a plain JavaScript object, -there is no need for further abstraction. +`meta()` and `links()` methods. Since meta and links are only a plain +JavaScript objects, there is no need for further abstraction. Since references are not observable by design, hooks are proposed in the accompanying [RFC #123](https://github.com/emberjs/rfcs/pull/123) which allow @@ -25,7 +25,7 @@ once a use case is flushed out and it's reasonable to being in core. #### In short - A new `links()` method is added to record, belongs-to, has-many and - record-array references which returns all associated links + document references which returns all associated links - Add `LinkObject` which is an abstraction for a single link and its properties (meta, href) - Add a `DocumentReference` which describes a JSON-API document and its @@ -170,13 +170,6 @@ need to be accesible for a record. Links are exposed in an object, with each link exposed with the following properties: -- `href` -- `meta`: optional meta for the link, or `null` if there is none - -```js -let links = post.hasMany('authors'); -``` - ```js /** Link @@ -366,6 +359,8 @@ export default Book; ## Sample code +/* + ### Pagination ```js @@ -384,7 +379,7 @@ store.query("book", { page: 1 }).then(function(books) { allPages.addObjects(books.toArray()); let booksRef = books.documentRef(); - let nextPage = booksRef.links("next"); + let nextPage = booksRef.links().next; // GET /books?page=2 nextPage.load().then(function(nextBooks) { @@ -393,6 +388,8 @@ store.query("book", { page: 1 }).then(function(books) { }); ``` +*/ + ### Meta for `findAll` ```js @@ -447,7 +444,7 @@ store.findRecord("book", 1).then(function(book) { # Drawbacks -- increase of low level, public API +- increase of low level, public API - though it extends the concept of references, it's a significant increase of API surface @@ -457,6 +454,7 @@ store.findRecord("book", 1).then(function(book) { # Unresolved questions +- what is the API to load a link? - since multiple "locations" of meta will be supported, there needs to be a new hook only within the `RESTSerializer` to extract record/document level meta, additionally to the `extractMeta` hook, which is invoked with the whole From 03f4bbdb4b6e49e16472f767e11c627604c417a9 Mon Sep 17 00:00:00 2001 From: pangratz Date: Thu, 20 Oct 2016 23:04:22 +0300 Subject: [PATCH 08/10] Correctly comment out (not yet relevant) sample code --- text/0000-ember-data-links-meta-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index b2c8909329..78413a7c64 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -359,7 +359,7 @@ export default Book; ## Sample code -/* +/** ### Pagination @@ -388,7 +388,7 @@ store.query("book", { page: 1 }).then(function(books) { }); ``` -*/ +**/ ### Meta for `findAll` From c675322bccff122d8062403f3a07432a8c3397db Mon Sep 17 00:00:00 2001 From: pangratz Date: Thu, 20 Oct 2016 23:05:28 +0300 Subject: [PATCH 09/10] Remove not relevant sample code --- ...0000-ember-data-links-meta-improvements.md | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index 78413a7c64..e5dd596b62 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -359,37 +359,6 @@ export default Book; ## Sample code -/** - -### Pagination - -```js -let allPages = Ember.A(); - -// GET /books?page=1 -// { -// data: […], -// links: { -// next: { -// href: "…" -// } -// } -// } -store.query("book", { page: 1 }).then(function(books) { - allPages.addObjects(books.toArray()); - - let booksRef = books.documentRef(); - let nextPage = booksRef.links().next; - - // GET /books?page=2 - nextPage.load().then(function(nextBooks) { - allPages.addObjects(nextBooks.toArray()); - }); -}); -``` - -**/ - ### Meta for `findAll` ```js From cd4e6111bbe714dd06c668ca5e9224ce1f97ec54 Mon Sep 17 00:00:00 2001 From: pangratz Date: Thu, 20 Oct 2016 23:26:55 +0300 Subject: [PATCH 10/10] Remove reference to DS.LinkReference --- text/0000-ember-data-links-meta-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-ember-data-links-meta-improvements.md b/text/0000-ember-data-links-meta-improvements.md index e5dd596b62..f1bed2e092 100644 --- a/text/0000-ember-data-links-meta-improvements.md +++ b/text/0000-ember-data-links-meta-improvements.md @@ -236,7 +236,7 @@ class DocumentReference { This is useful for pagination of resource collections for example. - @return {Array} + @return {Object} */ links()