Permalink
Browse files

feat(project): add identifier decorator to support the duplicated res…

…ources
  • Loading branch information...
mroseboom authored and RWOverdijk committed May 19, 2017
1 parent ea7346f commit 286453f979a63894be19af6cc901c80979559217
@@ -43,6 +43,20 @@ class HelloWorld {}
class HelloWorld {}
```
## @identifier()
This decorator comes in handy when you have the same resource name for multiple endpoints. Without it, aurelia-orm will use the resource decorator. The identifier will be used to register and retrieve the repository for the given entity.
When left empty, the name of the class (.toLowerCase()) will be used as the resource name. This is usually fine.
> **NOTE:** Leaving the decorator without a value is bad idea for bundling, because the bundler renames your modules it will not longer match. For more information see [bundling](https://github.com/SpoonX/aurelia-orm#bundling)
```js
// Sets the identifier to "i-want-bacon"
@identifier('i-want-bacon')
class HelloWorld {}
```
## @resource()
This decorator is probably the most important one. Without it, aurelia-orm won't know what your **custom entity** is all about. The resource maps to the API endpoint it represents. Simply put, resource `foo` maps to `/foo`.
@@ -0,0 +1,16 @@
import {OrmMetadata} from '../orm-metadata';
/**
* Set the 'identifierName' metadata on the entity
*
* @param {string} identifierName The name of the identifier
*
* @return {function}
*
* @decorator
*/
export function identifier(identifierName) {
return function(target) {
OrmMetadata.forTarget(target).put('identifier', identifierName || target.name.toLowerCase());
};
}
@@ -47,7 +47,9 @@ export class EntityManager {
* @chainable
*/
registerEntity(EntityClass) {
this.entities[OrmMetadata.forTarget(EntityClass).fetch('resource')] = EntityClass;
let meta = OrmMetadata.forTarget(EntityClass);
this.entities[meta.fetch('identifier') || meta.fetch('resource')] = EntityClass;
return this;
}
@@ -61,13 +63,18 @@ export class EntityManager {
* @throws {Error}
*/
getRepository(entity) {
let reference = this.resolveEntityReference(entity);
let resource = entity;
let reference = this.resolveEntityReference(entity);
let identifier = entity;
let resource = entity;
if (typeof reference.getResource === 'function') {
resource = reference.getResource() || resource;
}
if (typeof reference.getIdentifier === 'function') {
identifier = reference.getIdentifier() || resource;
}
if (typeof resource !== 'string') {
throw new Error('Unable to find resource for entity.');
}
@@ -90,11 +97,12 @@ export class EntityManager {
// Tell the repository instance what resource it should use.
instance.setMeta(metaData);
instance.resource = resource;
instance.identifier = identifier;
instance.entityManager = this;
if (instance instanceof DefaultRepository) {
// This is a default repository. We'll cache this instance.
this.repositories[resource] = instance;
this.repositories[identifier] = instance;
}
return instance;
@@ -133,13 +141,15 @@ export class EntityManager {
let reference = this.resolveEntityReference(entity);
let instance = this.container.get(reference);
let resource = reference.getResource();
let identifier = reference.getIdentifier() || resource;
if (!resource) {
if (typeof entity !== 'string') {
throw new Error('Unable to find resource for entity.');
}
resource = entity;
identifier = entity;
}
// Set the validator.
@@ -149,6 +159,8 @@ export class EntityManager {
instance.setValidator(validator);
}
return instance.setResource(resource).setRepository(this.getRepository(resource));
return instance.setResource(resource)
.setIdentifier(identifier)
.setRepository(this.getRepository(identifier));
}
}
@@ -407,6 +407,37 @@ export class Entity {
return this;
}
/**
* Get the identifier name of this entity's reference (static).
*
* @return {string|null}
*/
static getIdentifier() {
return OrmMetadata.forTarget(this).fetch('identifier');
}
/**
* Get the identifier name of this entity instance
*
* @return {string|null}
*/
getIdentifier() {
return this.__identifier || this.getMeta().fetch('identifier');
}
/**
* Set this instance's identifier.
*
* @param {string} identifier
*
* @return {Entity} itself
* @chainable
*/
setIdentifier(identifier) {
return this.define('__identifier', identifier);
}
/**
* Get the resource name of this entity's reference (static).
*
@@ -21,6 +21,7 @@ export class Metadata {
constructor() {
this.metadata = {
repository : DefaultRepository,
identifier : null,
resource : null,
endpoint : null,
name : null,
@@ -54,6 +54,28 @@ export class Repository {
return this.meta;
}
/**
* Set the identifier
*
* @param {string} identifier
* @return {Repository} this
* @chainable
*/
setIdentifier(identifier) {
this.identifier = identifier;
return this;
}
/**
* Get the identifier
*
* @return {string|null}
*/
getIdentifier() {
return this.identifier;
}
/**
* Set the resource
*
@@ -0,0 +1,40 @@
import {WithIdentifier} from '../resources/entity/with-identifier';
import {FirstWithSameResource, SecondWithSameResource} from '../resources/entity/with-same-resource';
import {FirstWithSameResourceAndIdentifier, SecondWithSameResourceAndIdentifier} from '../resources/entity/with-same-resource-and-identifier';
import {OrmMetadata} from '../../src/orm-metadata';
import {Entity} from '../../src/entity';
import {EntityManager} from '../../src/entity-manager';
import {Container} from 'aurelia-dependency-injection';
describe('@identifier()', function () {
it('Should add identifier on the entity', function () {
expect(OrmMetadata.forTarget(WithIdentifier).fetch('identifier')).toEqual('with-identifier');
});
it('Should override the first entity when the same resource key is used without the identifier decorator', function () {
let entityManager = new EntityManager(new Container());
entityManager.registerEntities([FirstWithSameResource, SecondWithSameResource]);
expect(Object.keys(entityManager.entities).length).toBe(1);
expect(entityManager.entities).toEqual({ 'with-duplicated-resource': SecondWithSameResource });
expect(entityManager.entities).not.toEqual({ 'with-duplicated-resource': FirstWithSameResource });
});
it('Should register two entities when the identifier decorator is applied with both the same resource name', function () {
let entityManager = new EntityManager(new Container());
entityManager.registerEntities([FirstWithSameResourceAndIdentifier, SecondWithSameResourceAndIdentifier]);
expect(Object.keys(entityManager.entities).length).toBe(2);
expect(OrmMetadata.forTarget(FirstWithSameResourceAndIdentifier).fetch('identifier')).toEqual('with-identifier-foo');
expect(OrmMetadata.forTarget(FirstWithSameResourceAndIdentifier).fetch('resource')).toEqual('with-duplicated-resource');
expect(OrmMetadata.forTarget(FirstWithSameResourceAndIdentifier).fetch('endpoint')).toEqual('with-endpoint-foo');
expect(OrmMetadata.forTarget(SecondWithSameResourceAndIdentifier).fetch('identifier')).toEqual('with-identifier-bar');
expect(OrmMetadata.forTarget(SecondWithSameResourceAndIdentifier).fetch('resource')).toEqual('with-duplicated-resource');
expect(OrmMetadata.forTarget(SecondWithSameResourceAndIdentifier).fetch('endpoint')).toEqual('with-endpoint-bar');
});
});
@@ -1,6 +1,7 @@
import {EntityManager} from '../src/entity-manager';
import {Container} from 'aurelia-dependency-injection';
import {WithResource} from './resources/entity/with-resource';
import {WithIdentifier} from './resources/entity/with-identifier';
import {WithCustomRepository} from './resources/entity/with-custom-repository';
import {SimpleCustom} from './resources/repository/simple-custom';
import {DefaultRepository} from '../src/default-repository';
@@ -16,6 +17,14 @@ describe('EntityManager', function() {
expect(entityManager.entities).toEqual({'with-resource': WithResource});
});
it('Should register entities with the manager and identifier decorator', function() {
let entityManager = new EntityManager(new Container());
entityManager.registerEntities([WithIdentifier]);
expect(entityManager.entities).toEqual({'with-identifier': WithIdentifier});
});
it('Should return self.', function() {
let entityManager = new EntityManager(new Container());
@@ -38,6 +47,14 @@ describe('EntityManager', function() {
expect(entityManager.entities).toEqual({'with-resource': WithResource});
});
it('Should register an entity with the manager and identifier decorator', function() {
let entityManager = new EntityManager(new Container());
entityManager.registerEntity(WithIdentifier);
expect(entityManager.entities).toEqual({'with-identifier': WithIdentifier});
});
xit('Should throw when register with non-Entity', function() {
let entityManager = new EntityManager(new Container());
class Wrong {}
@@ -72,6 +89,22 @@ describe('EntityManager', function() {
expect(entityManager.getRepository(WithResource) instanceof DefaultRepository).toBe(true);
});
it('Should return the default repository when no custom specified. (Entity resource)', function() {
let entityManager = new EntityManager(new Container());
entityManager.registerEntity(WithIdentifier);
expect(entityManager.getRepository('with-identifier') instanceof DefaultRepository).toBe(true);
});
it('Should return the default repository when no custom specified. (Entity reference)', function() {
let entityManager = new EntityManager(new Container());
entityManager.registerEntity(WithIdentifier);
expect(entityManager.getRepository(WithIdentifier) instanceof DefaultRepository).toBe(true);
});
it('Should return the custom repository when specified.', function() {
let entityManager = new EntityManager(new Container());
@@ -164,6 +197,20 @@ describe('EntityManager', function() {
expect(entityManager.getEntity('with-resource') instanceof WithResource).toBe(true);
});
it('Should return a new `WithIdentifier` instance (Entity reference).', function() {
let entityManager = new EntityManager(new Container());
expect(entityManager.getEntity(WithIdentifier) instanceof WithIdentifier).toBe(true);
});
it('Should return a new `WithIdentifier` instance (Entity resource).', function() {
let entityManager = new EntityManager(new Container());
entityManager.registerEntity(WithIdentifier);
expect(entityManager.getEntity('with-identifier') instanceof WithIdentifier).toBe(true);
});
it('Should return a new `Entity` instance.', function() {
let entityManager = new EntityManager(new Container());
@@ -9,6 +9,7 @@ describe('OrmMetadata', function() {
expect(meta instanceof Metadata).toBe(true);
expect(meta.metadata).toEqual({
repository: DefaultRepository,
identifier: null,
resource: null,
endpoint: null,
name: null,
@@ -24,6 +25,7 @@ describe('OrmMetadata', function() {
expect(meta instanceof Metadata).toBe(true);
expect(meta.metadata).toEqual({
repository: DefaultRepository,
identifier: null,
resource: null,
name: null,
endpoint: null,
@@ -76,6 +76,22 @@ describe('Repository', function() {
});
});
describe('.setIdentifier()', function() {
it('Should set the identifier.', function() {
let repository = new Repository(getApiConfig());
repository.setIdentifier('foo');
expect(repository.identifier).toBe('foo');
});
it('Should return self.', function() {
let repository = new Repository(getApiConfig());
expect(repository.setIdentifier('foo')).toBe(repository);
});
});
describe('.setResource()', function() {
it('Should set the resource.', function() {
let repository = new Repository(getApiConfig());
@@ -0,0 +1,11 @@
import {identifier} from '../../../src/decorator/identifier';
import {resource} from '../../../src/decorator/resource';
import {idProperty} from '../../../src/decorator/idProperty';
import {Entity} from '../../../src/entity';
@identifier('with-identifier')
@resource('with-resource')
@idProperty('idTag')
export class WithIdentifier extends Entity {
foo = null;
}
@@ -0,0 +1,20 @@
import {endpoint} from '../../../src/decorator/endpoint';
import {identifier} from '../../../src/decorator/identifier';
import {resource} from '../../../src/decorator/resource';
import {Entity} from '../../../src/entity';
@identifier('with-identifier-foo')
@resource('with-duplicated-resource')
@endpoint('with-endpoint-foo')
export class FirstWithSameResourceAndIdentifier extends Entity {
foo = null;
}
@identifier('with-identifier-bar')
@resource('with-duplicated-resource')
@endpoint('with-endpoint-bar')
export class SecondWithSameResourceAndIdentifier extends Entity {
foo = null;
}
@@ -0,0 +1,18 @@
import {endpoint} from '../../../src/decorator/endpoint';
import {identifier} from '../../../src/decorator/identifier';
import {resource} from '../../../src/decorator/resource';
import {Entity} from '../../../src/entity';
@resource('with-duplicated-resource')
@endpoint('with-endpoint-foo')
export class FirstWithSameResource extends Entity {
foo = null;
}
@resource('with-duplicated-resource')
@endpoint('with-endpoint-bar')
export class SecondWithSameResource extends Entity {
foo = null;
}

0 comments on commit 286453f

Please sign in to comment.