-
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #165 from badgateway/collection-json
Support for the Collection+JSON format
- Loading branch information
Showing
6 changed files
with
331 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import Link from '../link'; | ||
import Representation from './base'; | ||
|
||
type CjDocument = { | ||
collection: CjCollection, | ||
}; | ||
|
||
type CjCollection = { | ||
version?: string, | ||
href?: string, | ||
links?: CjLink[], | ||
items?: CjItem[], | ||
queries?: CjQuery[], | ||
template?: CjTemplate, | ||
error?: CjError | ||
}; | ||
|
||
type CjError = { | ||
title?: string, | ||
code?: string, | ||
message?: string, | ||
}; | ||
|
||
type CjTemplate = { | ||
data?: CjProperty[] | ||
}; | ||
|
||
type CjItem = { | ||
href?: string, | ||
data?: CjProperty[], | ||
links?: CjLink[], | ||
}; | ||
|
||
type CjProperty = { | ||
name: string, | ||
value?: string, | ||
prompt?: string | ||
}; | ||
|
||
type CjQuery = { | ||
href: string, | ||
rel: string, | ||
name?: string, | ||
prompt?: string, | ||
data?: CjProperty[] | ||
}; | ||
|
||
type CjLink = { | ||
href: string, | ||
rel: string, | ||
name?: string, | ||
render?: 'image' | 'link', | ||
prompt?: string | ||
}; | ||
|
||
/** | ||
* The Representation class is basically a 'body' of a request | ||
* or response. | ||
* | ||
* This class is for the Collection+JSON format, defined here: | ||
* | ||
* http://amundsen.com/media-types/collection/format/#object-collection | ||
*/ | ||
export default class CollectionJson extends Representation<CjDocument> { | ||
|
||
parse(body: string): CjDocument { | ||
|
||
return JSON.parse(body); | ||
|
||
} | ||
|
||
parseLinks(body: CjDocument): Link[] { | ||
|
||
const result: Link[] = []; | ||
if (body.collection.links !== undefined) { | ||
|
||
// Lets start with all links from the links property. | ||
for (const link of body.collection.links) { | ||
result.push(new Link({ | ||
context: this.uri, | ||
href: link.href, | ||
rel: link.rel, | ||
title: link.name, | ||
})); | ||
} | ||
} | ||
|
||
if (body.collection.items !== undefined) { | ||
|
||
// Things that are in the 'items' array should also be considered links | ||
// with the 'item' link relationship. | ||
for (const item of body.collection.items) { | ||
|
||
if (!item.href) { | ||
continue; | ||
} | ||
|
||
result.push(new Link({ | ||
context: this.uri, | ||
href: item.href, | ||
rel: 'item', | ||
})); | ||
} | ||
|
||
} | ||
|
||
|
||
if (body.collection.queries !== undefined) { | ||
|
||
// Things that are in the 'queries' array can be considered links too. | ||
for (const query of body.collection.queries) { | ||
|
||
if (!query.data) { | ||
// Non-templated | ||
result.push(new Link({ | ||
context: this.uri, | ||
href: query.href, | ||
rel: query.rel, | ||
title: query.name, | ||
})); | ||
} else { | ||
// This query has a data property so we need 50% more magic | ||
result.push(new Link({ | ||
context: this.uri, | ||
href: query.href + query.data.map( | ||
property => '{?' + property.name + '}' | ||
).join(''), | ||
templated: true, | ||
rel: query.rel, | ||
title: query.name, | ||
})); | ||
} | ||
} | ||
|
||
} | ||
|
||
return result; | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { expect } from 'chai'; | ||
import Link from '../../../src/link'; | ||
import CollectionJson from '../../../src/representor/collection-json'; | ||
|
||
describe('collection+json representor', () => { | ||
|
||
it('should parse the "Minimal Representation" example', () => { | ||
|
||
const exampleObj = { "collection" : | ||
{ | ||
"version" : "1.0", | ||
|
||
"href" : "http://example.org/friends/" | ||
} | ||
}; | ||
|
||
const cj = new CollectionJson( | ||
'http://example.org/friends/', | ||
'application/vnd.collection+json', | ||
JSON.stringify(exampleObj), | ||
new Map() | ||
); | ||
expect(cj.getLinks()).to.eql([]); | ||
|
||
}); | ||
|
||
it('should parse the "Collection Representation" example', () => { | ||
|
||
const exampleObj = { "collection" : | ||
{ | ||
"version" : "1.0", | ||
"href" : "http://example.org/friends/", | ||
|
||
"links" : [ | ||
{"rel" : "feed", "href" : "http://example.org/friends/rss"} | ||
], | ||
|
||
"items" : [ | ||
{ | ||
"href" : "http://example.org/friends/jdoe", | ||
"data" : [ | ||
{"name" : "full-name", "value" : "J. Doe", "prompt" : "Full Name"}, | ||
{"name" : "email", "value" : "[email protected]", "prompt" : "Email"} | ||
], | ||
"links" : [ | ||
{"rel" : "blog", "href" : "http://examples.org/blogs/jdoe", "prompt" : "Blog"}, | ||
{"rel" : "avatar", "href" : "http://examples.org/images/jdoe", "prompt" : "Avatar", "render" : "image"} | ||
] | ||
}, | ||
|
||
{ | ||
"href" : "http://example.org/friends/msmith", | ||
"data" : [ | ||
{"name" : "full-name", "value" : "M. Smith", "prompt" : "Full Name"}, | ||
{"name" : "email", "value" : "[email protected]", "prompt" : "Email"} | ||
], | ||
"links" : [ | ||
{"rel" : "blog", "href" : "http://examples.org/blogs/msmith", "prompt" : "Blog"}, | ||
{"rel" : "avatar", "href" : "http://examples.org/images/msmith", "prompt" : "Avatar", "render" : "image"} | ||
] | ||
}, | ||
|
||
{ | ||
"href" : "http://example.org/friends/rwilliams", | ||
"data" : [ | ||
{"name" : "full-name", "value" : "R. Williams", "prompt" : "Full Name"}, | ||
{"name" : "email", "value" : "[email protected]", "prompt" : "Email"} | ||
], | ||
"links" : [ | ||
{"rel" : "blog", "href" : "http://examples.org/blogs/rwilliams", "prompt" : "Blog"}, | ||
{"rel" : "avatar", "href" : "http://examples.org/images/rwilliams", "prompt" : "Avatar", "render" : "image"} | ||
] | ||
} | ||
], | ||
|
||
"queries" : [ | ||
{"rel" : "search", "href" : "http://example.org/friends/search", "prompt" : "Search", | ||
"data" : [ | ||
{"name" : "search", "value" : ""} | ||
] | ||
}, | ||
{"rel" : "get-new", "href" : "http://example.org/friends/new", "prompt" : "New friends" }, | ||
], | ||
|
||
"template" : { | ||
"data" : [ | ||
{"name" : "full-name", "value" : "", "prompt" : "Full Name"}, | ||
{"name" : "email", "value" : "", "prompt" : "Email"}, | ||
{"name" : "blog", "value" : "", "prompt" : "Blog"}, | ||
{"name" : "avatar", "value" : "", "prompt" : "Avatar"} | ||
|
||
] | ||
} | ||
} | ||
}; | ||
|
||
const cj = new CollectionJson( | ||
'http://example.org/friends/', | ||
'application/vnd.collection+json', | ||
JSON.stringify(exampleObj), | ||
new Map() | ||
); | ||
expect(cj.getLinks()).to.eql([ | ||
new Link({ | ||
rel: 'feed', | ||
href: 'http://example.org/friends/rss', | ||
context: 'http://example.org/friends/', | ||
}), | ||
new Link({ | ||
rel: 'item', | ||
href: 'http://example.org/friends/jdoe', | ||
context: 'http://example.org/friends/', | ||
}), | ||
new Link({ | ||
rel: 'item', | ||
href: 'http://example.org/friends/msmith', | ||
context: 'http://example.org/friends/', | ||
}), | ||
new Link({ | ||
rel: 'item', | ||
href: 'http://example.org/friends/rwilliams', | ||
context: 'http://example.org/friends/', | ||
}), | ||
new Link({ | ||
rel: 'search', | ||
href: 'http://example.org/friends/search{?search}', | ||
templated: true, | ||
context: 'http://example.org/friends/', | ||
}), | ||
new Link({ | ||
rel: 'get-new', | ||
href: 'http://example.org/friends/new', | ||
context: 'http://example.org/friends/', | ||
}), | ||
]); | ||
|
||
}); | ||
|
||
it('should correctly handle edge-cases', () => { | ||
|
||
const exampleObj = { "collection" : | ||
{ | ||
"version" : "1.0", | ||
"href" : "http://example.org/friends/", | ||
"items" : [ | ||
{ | ||
"data" : [ | ||
{"name" : "full-name", "value" : "J. Doe", "prompt" : "Full Name"}, | ||
{"name" : "email", "value" : "[email protected]", "prompt" : "Email"} | ||
], | ||
"links" : [ | ||
{"rel" : "blog", "href" : "http://examples.org/blogs/jdoe", "prompt" : "Blog"}, | ||
{"rel" : "avatar", "href" : "http://examples.org/images/jdoe", "prompt" : "Avatar", "render" : "image"} | ||
] | ||
}, | ||
], | ||
} | ||
}; | ||
|
||
const cj = new CollectionJson( | ||
'http://example.org/friends/', | ||
'application/vnd.collection+json', | ||
JSON.stringify(exampleObj), | ||
new Map() | ||
); | ||
expect(cj.getLinks()).to.eql([]); | ||
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters