Skip to content
Merged
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
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@
"tslib": "1.6.0",
"typescript": "<2.10.0",
"uglify-js": "^3.1.9",
"jest-localstorage-mock": "^2.4.0",
"zone.js": "^0.8.12"
},
"dependencies": {
Expand Down
1 change: 0 additions & 1 deletion src/common.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { isLive, isCollection, isResource } from './common';
import { ICacheable } from './interfaces/cacheable';
import { Resource } from './resource';
import { DocumentCollection } from './document-collection';
import { DocumentResource } from './document-resource';

Expand Down
4 changes: 2 additions & 2 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export function isLive(cacheable: ICacheable, ttl: number = null): boolean {
}

export function isCollection(document: DocumentResource | DocumentCollection): document is DocumentCollection {
return !('id' in document.data);
return document !== null && 'data' in document && document.data !== null && !('id' in document.data);
}

export function isResource(document: DocumentResource | DocumentCollection): document is DocumentResource {
return 'id' in document.data;
return document !== null && 'data' in document && document.data !== null && 'id' in document.data;
}

// NOTE: Checks that the service passed to the method is registered (method needs to have service's type or a resource as first arg)
Expand Down
2 changes: 0 additions & 2 deletions src/document-collection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { DocumentCollection } from './document-collection';
import { Resource } from './resource';
import { IDataCollection } from './interfaces/data-collection';
import { Converter } from './services/converter';

describe('document-collection', () => {
let collection = new DocumentCollection();
Expand Down
7 changes: 4 additions & 3 deletions src/document-collection.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IResourcesByType } from './interfaces';
import { Resource } from './resource';
import { Page } from './services/page';
import { Document } from './document';
Expand Down Expand Up @@ -25,8 +26,8 @@ export class DocumentCollection<R extends Resource = Resource> extends Document
return null;
}

public fill(data_collection: IDataCollection): void {
let included_resources = Converter.buildIncluded(data_collection);
public fill(data_collection: IDataCollection, included_resources?: IResourcesByType): void {
included_resources = included_resources || Converter.buildIncluded(data_collection);

// sometimes get Cannot set property 'number' of undefined (page)
if (this.page && data_collection.meta) {
Expand All @@ -42,7 +43,7 @@ export class DocumentCollection<R extends Resource = Resource> extends Document
this.builded = data_collection.data && data_collection.data.length === 0;
for (let dataresource of data_collection.data) {
let res = this.find(dataresource.id) || Converter.getService(dataresource.type).getOrCreateResource(dataresource.id);
res.fill({ data: dataresource } /* , included_resources */); // @todo check with included resources?
res.fill({ data: dataresource }, included_resources);
new_ids[dataresource.id] = dataresource.id;
this.data.push(<R>res);
if (Object.keys(res.attributes).length > 0) {
Expand Down
54 changes: 43 additions & 11 deletions src/document-resource.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,54 @@
import { DocumentResource } from './document-resource';
import { Resource } from './resource';
import { Page } from './services/page';
import { Document } from './document';
import { IDataObject } from './interfaces/data-object';
import { Resource } from './resource';
import { Converter } from './services/converter';
import { IRelationships } from './interfaces/relationship';
import { DocumentCollection } from './document-collection';
import { Service } from './service';

export class MockResource extends Resource {
public attributes = {
name: '',
description: ''
};
public type = 'resource';

public relationships: IRelationships = {
resource: new DocumentResource<MockResource>(),
collection: new DocumentCollection<MockResource>()
};
}

class MockResourcesService extends Service<MockResource> {
public type = 'resource';
public resource = MockResource;
}

describe('document resource', () => {
let document_resource = new DocumentResource();
let document_resource;

beforeEach(() => {
document_resource = new DocumentResource();
});

it('should be created', () => {
expect(document_resource.builded).toBe(false);
expect(document_resource.content).toBe('id');
});
it('data property should have a new resource instance', () => {
let Resource_spy = spyOn(Resource, 'constructor');
let resource = new Resource();
const resource = new Resource();
expect(document_resource.data).toEqual(resource);
});
it('page property should have a new page instance', () => {
let page = new Page();
const page = new Page();
expect(document_resource.page).toEqual(page);
});
it('fill mehotd should call Reource class fill mehtod with the passed IDataObject parameter and fill meta property', () => {
let Resource_fill_spy = spyOn(document_resource.data, 'fill');
it('fill method should call Resource class fill method with the passed IDataObject parameter and fill meta property', () => {
const mockResourceService = new MockResourcesService();
const resource = new MockResource();
spyOn(mockResourceService, 'getOrCreateResource').and.returnValue(resource);
spyOn(Converter, 'getService').and.returnValue(mockResourceService);
const Resource_fill_spy = spyOn(resource, 'fill');
document_resource.fill({
data: {
type: 'data',
Expand All @@ -31,9 +59,13 @@ describe('document resource', () => {
expect(Resource_fill_spy).toHaveBeenCalled();
expect(document_resource.meta).toEqual({ meta: 'meta' });
});
it('if passed IDataObject has no meta property, fill mehotd should should assign an empty Object', () => {
it('if passed IDataObject has no meta property, fill method should should assign an empty Object', () => {
document_resource.meta = null;
let Resource_fill_spy = spyOn(document_resource.data, 'fill');
const mockResourceService = new MockResourcesService();
const resource = new MockResource();
spyOn(mockResourceService, 'getOrCreateResource').and.returnValue(resource);
spyOn(Converter, 'getService').and.returnValue(mockResourceService);
const Resource_fill_spy = spyOn(resource, 'fill');
document_resource.fill({
data: {
type: 'data',
Expand Down
5 changes: 5 additions & 0 deletions src/document-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Resource } from './resource';
import { Page } from './services/page';
import { Document } from './document';
import { IDataObject } from './interfaces/data-object';
import { Converter } from './services/converter';

export class DocumentResource<R extends Resource = Resource> extends Document {
public data: R = <R>new Resource(); // @todo?
Expand All @@ -11,6 +12,10 @@ export class DocumentResource<R extends Resource = Resource> extends Document {
public page = new Page();

public fill(data_resource: IDataObject): void {
if ((!this.data.type && !this.data.id) && (data_resource.data.type && data_resource.data.id)) {
this.data = <R>Converter.getService(data_resource.data.type).getOrCreateResource(data_resource.data.id);
}

this.data.fill(data_resource);
this.meta = data_resource.meta || {};
}
Expand Down
6 changes: 2 additions & 4 deletions src/resource.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { DocumentCollection } from './document-collection';
import { IDataObject } from 'src/interfaces/data-object';
import { Base } from 'src/services/base';
import { IParamsResource } from 'src/interfaces/params-resource';
import { IDataObject } from './interfaces/data-object';
import { IParamsResource } from './interfaces';
import { DocumentResource } from './document-resource';
import { Core } from './core';
import { PathBuilder } from './services/path-builder';
import { Resource } from './resource';
import { IDataCollection } from './interfaces/data-collection';
import { of } from 'rxjs';

describe('resource', () => {
Expand Down
33 changes: 26 additions & 7 deletions src/resource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Core } from './core';
import { IResourcesByType } from './interfaces/resources-by-type';
import { IDataResource } from './interfaces/data-resource';
import { IResourcesByType } from './interfaces';
import { Service } from './service';
import { Base } from './services/base';
import { PathBuilder } from './services/path-builder';
Expand Down Expand Up @@ -67,10 +68,19 @@ export class Resource implements ICacheable {
relationships[relation_alias].data.push(reational_object);

// no se agregó aún a included && se ha pedido incluir con el parms.include
let temporal_id = resource.type + '_' + resource.id;
let temporal_id = `${resource.type}_${resource.id}`;
if (included_ids.indexOf(temporal_id) === -1 && params.include.indexOf(relation_alias) !== -1) {
included_ids.push(temporal_id);
included.push(resource.toObject({}).data);
const {
data: data,
included: included_resources
}: { data: IDataResource; included?: Array<any> } = resource.toObject(params);

included_ids = [
...included_ids,
temporal_id,
...(included_resources || []).map((result: IDataResource) => `${result.type}_${result.id}`)
];
included = [...included, data, ...(included_resources || [])];
}
}
} else {
Expand Down Expand Up @@ -102,10 +112,19 @@ export class Resource implements ICacheable {
}

// no se agregó aún a included && se ha pedido incluir con el parms.include
let temporal_id = relationship_data.type + '_' + relationship_data.id;
let temporal_id = `${relationship_data.type}_${relationship_data.id}`;
if (included_ids.indexOf(temporal_id) === -1 && params.include.indexOf(relation_alias) !== -1) {
included_ids.push(temporal_id);
included.push(relationship_data.toObject({}).data);
const {
data: data,
included: included_resources
}: { data: IDataResource; included?: Array<any> } = relationship_data.toObject(params);

included_ids = [
...included_ids,
temporal_id,
...(included_resources || []).map((result: IDataResource) => `${result.type}_${result.id}`)
];
included = [...included, data, ...(included_resources || [])];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class Service<R extends Resource = Resource> {
}

public getOrCreateResource(id: string): R {
let service = Converter.getService(this.type);
let service = this.getService();
if (service.cachememory && id in service.cachememory.resources) {
return <R>service.cachememory.resources[id];
} else {
Expand Down
59 changes: 47 additions & 12 deletions src/services/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,28 @@
import { Core } from '../core';
import { Resource } from '../resource';
import { Service } from '../service';
import { IResourcesByType, IObjectsById } from '../interfaces';
import { IObjectsById, IResourcesByType } from '../interfaces';
import { IDataObject } from '../interfaces/data-object';
import { IDataCollection } from '../interfaces/data-collection';
import { IDataResource } from '../interfaces/data-resource';
import { isDevMode } from '@angular/core';
import { DocumentResource } from '../document-resource';
import { DocumentCollection } from '../document-collection';

export class Converter<R extends Resource> {
/*
Convert json arrays (like included) to an indexed Resources array by [type][id]
*/
public static json_array2resources_array_by_type(json_array: Array<IDataResource>): IResourcesByType {
let all_resources: IObjectsById<Resource> = {};
let resources_by_type: IResourcesByType = {};
const all_resources = Converter.json_array2resources_array(json_array, true);

Converter.json_array2resources_array(json_array, all_resources);
for (const key in all_resources) {
let resource = all_resources[key];
return Converter.resources_by_type(Object.values(all_resources));
}

protected static resources_by_type(resources: Array<Resource>): IResourcesByType {
let resources_by_type: IResourcesByType = {};
for (const key in resources) {
let resource = resources[key];

if (!(resource.type in resources_by_type)) {
resources_by_type[resource.type] = {};
Expand All @@ -29,10 +34,33 @@ export class Converter<R extends Resource> {
return resources_by_type;
}

public static json2resource(json_resource: IDataResource, instance_relationships): Resource {
public static json2resource(json_resource: IDataResource, instance_relationships: boolean): Resource {
let resource_service = Converter.getService(json_resource.type);
if (resource_service) {
return Converter.procreate(json_resource);
const resource = Converter.procreate(json_resource);
if (instance_relationships) {
for (const relationshipName in resource.relationships) {
if (Array.isArray(resource.relationships[relationshipName].data)) {
const resourceData = (resource.relationships[relationshipName].data as Array<Resource>)
.map(singleResourceData => Converter.procreate(singleResourceData));
const documentCollection = new DocumentCollection();
documentCollection.fill({data: resourceData});
resource.relationships[relationshipName] = documentCollection;
} else {
const resourceData = resource.relationships[relationshipName].data as IDataResource;
if (!resourceData || !resourceData.id || !resourceData.type) {
continue;
}

const relationshipResource = Converter.procreate(resourceData);
const documentResource = new DocumentResource();
documentResource.fill({data: relationshipResource});
resource.relationships[relationshipName] = documentResource;
}
}
}

return resource;
} else {
if (isDevMode()) {
console.warn(
Expand Down Expand Up @@ -76,8 +104,8 @@ export class Converter<R extends Resource> {
resource = Converter.getService(data.type).getOrCreateResource(data.id);
}

resource.attributes = data.attributes || {};
resource.relationships = <{ [key: string]: any }>data.relationships;
resource.attributes = { ...(resource.attributes || {}), ...data.attributes };
resource.relationships = ({ ...(resource.relationships || {}), ...data.relationships });
resource.is_new = false;

return resource;
Expand All @@ -86,10 +114,17 @@ export class Converter<R extends Resource> {
/*
Convert json arrays (like included) to an Resources arrays without [keys]
*/
private static json_array2resources_array(json_array: Array<IDataResource>, destination_array: IObjectsById<Resource> = {}): void {
private static json_array2resources_array(
json_array: Array<IDataResource>,
instance_relationships: boolean = false
): IObjectsById<Resource> {
let destination_array: IObjectsById<Resource> = {};

for (let data of json_array) {
let resource = Converter.json2resource(data, false);
let resource = Converter.json2resource(data, instance_relationships);
destination_array[resource.type + '_' + resource.id] = resource;
}

return destination_array;
}
}
6 changes: 3 additions & 3 deletions src/services/resource-relationships-converter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { ResourceRelationshipsConverter } from './resource-relationships-converter';
import { DocumentCollection } from 'src/document-collection';
import { DocumentCollection } from '../document-collection';
import { CacheStore } from '../services/cachestore';
import { CacheMemory } from '../services/cachememory';
import { Converter } from './converter';
import { Service } from '../service';
import { DocumentResource } from '../document-resource';
import { Resource } from '../resource';
import { IResourcesByType } from '../interfaces';
import { IRelationships } from '../interfaces/relationship';

function clone(obj) {
Expand Down Expand Up @@ -71,11 +70,12 @@ describe('ResourceRelationshipsConverter', () => {

it('should set builded to true when a hasOne relationsihp is builded', () => {
spyOn(CacheStore.prototype, 'setResource');
spyOn(Converter, 'getService').and.callFake(getService);
resource_relationships_converter.buildRelationships();
expect((resource_relationships_converter as any).relationships_dest.resource.builded).toBeTruthy();
});

it(`buildRelationships method should add hasMany and hasOne relationships to relationships_dest as appropiapte
it(`buildRelationships method should add hasMany and hasOne relationships to relationships_dest as appropriate
using relationships_from data`, () => {
// set up spy
spyOn(Converter, 'getService').and.callFake(getService);
Expand Down
5 changes: 3 additions & 2 deletions src/services/resource-relationships-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class ResourceRelationshipsConverter {
return;
}

(<DocumentCollection>this.relationships_dest[relation_alias]).fill(relation_from_value);
(<DocumentCollection>this.relationships_dest[relation_alias]).fill(relation_from_value, this.included_resources);
}

private __buildRelationshipHasOne(relation_data_from: IDataObject, relation_alias: string): void {
Expand All @@ -81,7 +81,8 @@ export class ResourceRelationshipsConverter {
return;
}

if (relation_data_from.data.id !== (<Resource>this.relationships_dest[relation_alias].data).id) {
if (!this.relationships_dest[relation_alias].data
|| relation_data_from.data.id !== (<Resource>this.relationships_dest[relation_alias].data).id) {
this.relationships_dest[relation_alias].data = new Resource();
}

Expand Down
Loading