Skip to content

Commit

Permalink
@dandi/hal and @dandi/mvc-hal: fix resource metadata getting mixed up…
Browse files Browse the repository at this point in the history
… when resources use inheritance
  • Loading branch information
DanielSchaffer committed Sep 28, 2018
1 parent 95f12ff commit 313f43b
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 16 deletions.
13 changes: 13 additions & 0 deletions hal/src/resource.decorator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,17 @@ describe('ResourceDecorator', () => {
expect(Resource.isResource(new Pinocchio())).to.be.false;
});
});

describe('when dealing with inheritance', () => {
@Resource()
class IsARealResource extends ModelBase {
constructor(source?: any) {
super(source);
}
}

it('still returns true when passed a class constructor with resource metadata', () => {
expect(Resource.isResource(IsARealResource), 'IsARealResource').to.be.true;
});
});
});
4 changes: 2 additions & 2 deletions hal/src/resource.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Constructor } from '@dandi/common';

import { getResourceMetadata, RESOURCE_META_KEY } from './resource.metadata';
import { getResourceMetadata, resourceMetaKey } from './resource.metadata';

export interface ResourceDecorator {
(): ClassDecorator;
Expand All @@ -17,7 +17,7 @@ Resource.isResource = function isResource(obj: any): boolean {
return false;
}
const target = typeof obj === 'function' ? obj : obj.constructor;
const meta = Reflect.get(target, RESOURCE_META_KEY);
const meta = Reflect.get(target, resourceMetaKey(target));
return !!(meta && meta.resource === target);
};

Expand Down
17 changes: 10 additions & 7 deletions hal/src/resource.metadata.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Constructor, getMetadata, isConstructor, MethodTarget } from '@dandi/common';
import { Constructor, getMetadata, isConstructor } from '@dandi/common';

import { globalSymbol } from './global.symbol';

export const RESOURCE_META_KEY = globalSymbol('meta:Resource');
export function resourceMetaKey(target: Constructor<any>): symbol {
return globalSymbol(`meta:Resource:${target.name}`);
}

export interface ResourceAccessorMetadata {
resource?: Constructor<any>;
Expand All @@ -17,6 +19,7 @@ export interface ResourceMetadata {
getAccessor?: ResourceAccessorMetadata;
listAccessor?: ResourceAccessorMetadata;
relations: { [rel: string]: ResourceRelationMetadata };
parent?: ResourceMetadata;
}

export interface ResourceRelationMetadata {
Expand All @@ -25,15 +28,15 @@ export interface ResourceRelationMetadata {
idProperty?: string;
}

export function getResourceMetadata(target: Constructor<any> | MethodTarget<any>): ResourceMetadata {
const ctr = isConstructor(target) ? target : target.constructor as Constructor<any>;
export function getResourceMetadata(obj: any): ResourceMetadata {
const target = isConstructor(obj) ? obj : (obj.constructor as Constructor<any>);
return getMetadata<ResourceMetadata>(
RESOURCE_META_KEY,
resourceMetaKey(target),
() => {
const meta = { resource: ctr, relations: {}, idProperty: null };
const meta: ResourceMetadata = { resource: target, relations: {}, idProperty: null };
const targetParent = Object.getPrototypeOf(target);
if (targetParent !== Object && targetParent.name) {
Object.setPrototypeOf(meta, getResourceMetadata(targetParent));
meta.parent = getResourceMetadata(targetParent);
}
return meta;
},
Expand Down
4 changes: 2 additions & 2 deletions mvc-hal/src/default.resource.composer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Constructor, Disposable } from '@dandi/common';
import { Disposable } from '@dandi/common';
import { Inject, Injectable, Repository, Resolver, ResolverContext } from '@dandi/core';
import {
ITEMS_RELATION,
Expand Down Expand Up @@ -66,7 +66,7 @@ export class DefaultResourceComposer implements ResourceComposer {
}

const result = new ComposedResource(resource);
const meta = getResourceMetadata(resource.constructor as Constructor<any>);
const meta = getResourceMetadata(resource);
context.relStack.push('self');

Object.keys(meta.relations).forEach((rel) => {
Expand Down
2 changes: 1 addition & 1 deletion mvc-hal/src/hal.result.transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class HalResultTransformer implements ControllerResultTransformer {
const context: CompositionContext = CompositionContext.for(
SELF_RELATION,
this.request.path,
embeddedRels ? embeddedRels.split(',') : [],
embeddedRels ? (Array.isArray(embeddedRels) ? embeddedRels : embeddedRels.split(',')) : [],
);
if (!Resource.isResource(result.resultObject)) {
return result;
Expand Down
4 changes: 2 additions & 2 deletions mvc-hal/src/resource.accessor.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Constructor, getMetadata, MethodTarget } from '@dandi/common';
import { getResourceMetadata, RESOURCE_META_KEY, ResourceAccessorMetadata } from '@dandi/hal';
import { getResourceMetadata, ResourceAccessorMetadata, resourceMetaKey } from '@dandi/hal';

import { globalSymbol } from './global.symbol';

Expand All @@ -23,7 +23,7 @@ export function resourceAccessor(resource: Constructor<any>, target: MethodTarge
meta.getAccessor.resource = resource;

// also set a reference to the metadata on the method itself so it can be retrieved and updated by resourceIdDecorator
getMetadata(RESOURCE_META_KEY, () => meta, target[propertyKey]);
getMetadata(resourceMetaKey(resource), () => meta, target[propertyKey]);
}

export function ResourceAccessor(resource: Constructor<any>) {
Expand Down
4 changes: 2 additions & 2 deletions mvc-hal/src/resource.list.accessor.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Constructor, getMetadata, MethodTarget } from '@dandi/common';
import { getResourceMetadata, RESOURCE_META_KEY } from '@dandi/hal';
import { getResourceMetadata, resourceMetaKey } from '@dandi/hal';

import { getAccessorMetadata } from './resource.accessor.decorator';

Expand All @@ -9,7 +9,7 @@ export function resourceListAccessor(resource: Constructor<any>, target: MethodT
meta.listAccessor.resource = resource;

// also set a reference to the metadata on the method itself so it can be retrieved and updated by resourceIdDecorator
getMetadata(RESOURCE_META_KEY, () => meta, target[propertyKey]);
getMetadata(resourceMetaKey(resource), () => meta, target[propertyKey]);
}

export function ResourceListAccessor(resource: Constructor<any>) {
Expand Down

0 comments on commit 313f43b

Please sign in to comment.