Skip to content
This repository was archived by the owner on Oct 7, 2021. It is now read-only.
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 .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
root = true

[*]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file must no be on the project.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove it. But I suggest you think about using EditorConfig as is makes collaborating way more easier. I use it in my projects all the time. And I guess it's some kind of common sense.

Have a look here: http://editorconfig.org/

indent_style = space
indent_size = 2
charset = utf-8
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ node_modules
dist
coverage
html-report

*.iml
.idea/
19 changes: 12 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"url": "http://github.com/deblockt/hal-rest-client/issues"
},
"dependencies": {
"@types/lodash": "^4.14.92",
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this lib very usefull?
I'm trying to have a minimal dependency number.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion it is. It adds the aspect of functional programming. It makes the code easier to read and to maintain. Since I'm a Java developer, this is something I miss sadly in TypeScript. I understand that you want to keep your footprint as small as possible and I can definitely rewrite the code to be imperative and if you insist, I'll do. But it'll make a tear run down my cheek. ;)

"axios": "^0.15.3",
"reflect-metadata": "^0.1.9",
"uri-templates": "^0.1.9"
Expand Down
2 changes: 1 addition & 1 deletion src/hal-json-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export class JSONParser {
for (const linkKey in json._links) {
if ("self" !== linkKey) {
const href = this.extractURI(links[linkKey]);
const type = Reflect.getMetadata("halClient:specificType", c.prototype, linkKey) || HalResource;
const propKey = halToTs[linkKey] || linkKey;
const type = Reflect.getMetadata("halClient:specificType", c.prototype, propKey) || HalResource;
const linkResource = createResource(this.halRestClient, type, href);
for (const propInLinkKey of Object.keys(links[linkKey])) {
linkResource.prop(propInLinkKey, links[linkKey][propInLinkKey]);
Expand Down
12 changes: 12 additions & 0 deletions src/hal-resource-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ export interface IHalResource {
*/
fetch(forceOrParams: boolean|object): Promise<this>;

/**
* fetch an array of the current resource
*
* Unlike #fetch(boolean|object) this method needs #uri to be set. So it's not possible to fetch a resource in
* advance.
*
* @param {object} params: If the uri is a template link, you can set the parameters.
* @param {IHalResourceConstructor<this>} resource: An optional resource to create the array items of.
* @returns {Promise<this[]>} Will return an array of resources.
*/
fetchArray(params?: object, resource?: IHalResourceConstructor<this>): Promise<this[]>;

/**
* get or set a prop or a link.
* if name is a link. link function is used
Expand Down
34 changes: 29 additions & 5 deletions src/hal-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DefaultSerializer, IJSONSerializer } from "./hal-json-serializer";
import { IHalResource, IHalResourceConstructor } from "./hal-resource-interface";
import { HalRestClient } from "./hal-rest-client";
import { URI } from "./uri";
import * as _ from 'lodash';

export class HalResource implements IHalResource {
public readonly links = {};
Expand Down Expand Up @@ -39,6 +40,13 @@ export class HalResource implements IHalResource {
}
}

public fetchArray(params?: object, resource?: IHalResourceConstructor<this>): Promise<this[]> {
return this.restClient.fetchArray(
this.uri.fill(params as object),
resource ? resource.prototype.constructor : this.constructor as IHalResourceConstructor<this>,
) as Promise<this[]>;
}

/**
* to clear value use null not undefined
*/
Expand Down Expand Up @@ -133,11 +141,8 @@ export class HalResource implements IHalResource {

for (const prop of props) {
const jsonKey = this.tsProptoHalProd(prop) ;
if (this.props[prop] !== undefined && this.props[prop] !== null && this.props[prop].onInitEnded !== undefined) {
json[jsonKey] = serializer.parseResource(this.props[prop]);
} else {
json[jsonKey] = serializer.parseProp(this.props[prop]);
}

json[jsonKey] = this.serializeProperty(prop, serializer);
}

for (const link of links) {
Expand All @@ -147,4 +152,23 @@ export class HalResource implements IHalResource {

return json;
}

private serializeProperty(prop, serializer: IJSONSerializer, arrayItem?: any) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a correct place for this method.
There are a hal-json-serializer file to serialize objects.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an extension to serialize(...) . It is just the part that I tool from serialize(...) to make it callable recursively (lines 143-147). And I added the ability to process arrays. Maybe the name should be something else.

Line 143 of the previous version corresponds to line 161. I used some helper methods of lodash to improve readability.
Old line 144 equals new line 162.
So is old line 146 to new line 169. They are the same.
In between is the ability to read arrays (and the recursive call).

let result = null;

const property = arrayItem || this.props[prop];

if (!_.isEmpty(property) && _.isFunction(property.onInitEnded)) {
result = serializer.parseResource(property)
} else if (_.isArray(property)) {
result = _(property)
.map(item => this.serializeProperty(prop, serializer, item))
.toArray()
.value()
} else {
result = serializer.parseProp(property)
}

return result
}
}
17 changes: 17 additions & 0 deletions src/test/model/array-resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {HalResource} from '../../hal-resource';
import {HalProperty} from '../../hal-decorator';

export class ArrayResourceItem extends HalResource {

@HalProperty()
public id: Number;

}

export class ArrayResource extends HalResource {

@HalProperty('items', ArrayResourceItem)
public items: ArrayResourceItem[];

}

28 changes: 28 additions & 0 deletions src/test/test-resource-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { test } from "tape-async";
import { ContactInfos } from "./model/contact-infos";
import { Cyclical, CyclicalList } from "./model/cyclical";
import { Person } from "./model/person";
import {ArrayResource, ArrayResourceItem} from './model/array-resource';

// mock list response
function initTests() {
Expand Down Expand Up @@ -122,6 +123,20 @@ function initTests() {
testNock
.get("/cyclicals/refresh")
.reply(200, cyclicals);

const arrayResource = {
_links: {
self: 'http://test.fr/array',
},
items: [
{id: 1, _links: {self: {href: 'http://test.fr/array/1'}}},
{id: 2, _links: {self: {href: 'http://test.fr/array/2'}}},
],
};

testNock
.get("/arrayResource")
.reply(200, arrayResource);
}

test("can get single string prop", async (t) => {
Expand Down Expand Up @@ -267,3 +282,16 @@ test("cyclical property have good class type", async (t) => {
t.ok(Array.isArray(cyclicals.cyclicals), "cyclicals is an array");
t.equals(cyclicals.cyclicals[0].property, "name");
});

test('fetching resource with array contains valid typed resources', async (t) => {
initTests();

const client = createClient('http://test.fr');
const resource: ArrayResource = await client.fetch('/arrayResource', ArrayResource);

t.ok(resource instanceof ArrayResource, 'Result has the correct type');
t.ok(Array.isArray(resource.items), 'Result content is array');
t.ok(resource.items.length === 2, 'Correct count of array items');
t.ok(resource.items[0] instanceof ArrayResourceItem, 'Array item has correct type');
t.ok(resource.items[0].id === 1, 'Correct item returned.');
});