Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous fromJSON in hal #471

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/state/base-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ export class BaseState<T> extends BaseHeadState implements State<T> {

}

toJSON() {

return this.data;

}

/**
* Certain formats can embed other resources, identified by their
* own URI.
Expand Down
36 changes: 28 additions & 8 deletions src/state/hal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import * as hal from 'hal-types';
/**
* Represents a resource state in the HAL format
*/
export class HalState<T = any> extends BaseState<T> {
export class HalState<T extends Record<string, any> = any> extends BaseState<T> {

serializeBody(): string {

return JSON.stringify({
toJSON(): hal.HalResource<T> {
return {
_links: this.serializeLinks(),
...this.data
});
};
}

serializeBody(): string {

return JSON.stringify(this.toJSON());

}

Expand Down Expand Up @@ -75,6 +79,22 @@ export const factory:StateFactory = async (client, uri, response): Promise<HalSt

const body = await response.json();
const links = parseLink(uri, response.headers.get('Link'));
const headers = response.headers;

return buildState(client, uri, body, links, headers);

};

export const fromJSON = (client: Client, body: hal.HalResource, uri?: string) : HalState => {

uri = uri || body._links.self.href;
const links = new Links(uri);
const headers = new Headers();
return buildState(client, uri, body, links, headers);

};

export const buildState = (client: Client, uri: string, body: hal.HalResource, links: Links, headers: Headers) : HalState => {

// The HAL factory is also respondible for plain JSON, which might be an
// array.
Expand All @@ -83,7 +103,7 @@ export const factory:StateFactory = async (client, uri, response): Promise<HalSt
client,
uri,
data: body,
headers: response.headers,
headers,
links,
});
}
Expand All @@ -102,9 +122,9 @@ export const factory:StateFactory = async (client, uri, response): Promise<HalSt
client,
uri: uri,
data: newBody,
headers: response.headers,
headers,
links: links,
embedded: parseHalEmbedded(client, uri, body, response.headers),
embedded: parseHalEmbedded(client, uri, body, headers),
actions: parseHalForms(uri, body),
});

Expand Down
7 changes: 6 additions & 1 deletion src/state/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export type State<T = any> = {
*/
serializeBody(): Buffer|Blob|string;

/**
* Returns a JSON of the state that can be used in a HTTP response.
*/
toJSON(): any;

/**
* Content-headers are a subset of HTTP headers that related directly
* to the content. The obvious ones are Content-Type.
Expand Down Expand Up @@ -116,7 +121,7 @@ export type State<T = any> = {
* Some information in HEAD responses might be available, but many aren't.
* Notably, the body.
*/
export type HeadState = Omit<State, 'data' | 'action' | 'actions' | 'hasAction' | 'serializeBody' | 'getEmbedded' | 'client' | 'clone'>;
export type HeadState = Omit<State, 'data' | 'action' | 'actions' | 'hasAction' | 'serializeBody' | 'toJSON' | 'getEmbedded' | 'client' | 'clone'>;

/**
* A 'StateFactory' is responsible for taking a Fetch Response, and returning
Expand Down
34 changes: 33 additions & 1 deletion test/unit/state/hal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { factory } from '../../../src/state/hal';
import { factory, fromJSON } from '../../../src/state/hal';
import { Client } from '../../../src';

describe('HAL state factory', () => {
Expand Down Expand Up @@ -229,6 +229,38 @@ describe('HAL state factory', () => {
happy: 2020,
});

});
it('should correctly rebuild HAL documents', async() => {

const base = {
_links: {
self: {
href: '/foo',
},
author: {
href: 'https://evertpot.com/',
},
foo: [
{
href: '/bar',
},
{
href: '/bar2',
},
{
href: '/bar3',
},
]
},
happy: 2020,
};
const hal = await callFactory(base, base._links.self.href);
// const json = (hal as HalState).toJSON();
const json = hal.toJSON();
const result = fromJSON(hal.client, json);
expect(result.uri).to.eql(hal.uri);
expect(JSON.parse(result.serializeBody())).to.eql(base);

});
it('should handle JSON documents that are arrays', async () => {

Expand Down