Skip to content

Conversation

@raymondfeng
Copy link
Contributor

@raymondfeng raymondfeng commented Apr 9, 2018

This PR introduces a strong-typed MetadataKey for metadata access via reflection. It's inspired by #1169.

Checklist

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

@raymondfeng raymondfeng requested a review from bajtos as a code owner April 9, 2018 22:40
@raymondfeng raymondfeng changed the title Metadata key Introduce strongly-typed metadata key Apr 10, 2018
Copy link
Contributor

@shimks shimks left a comment

Choose a reason for hiding this comment

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

Had a quick overview, and most of it looked good. A couple of points though:

  • MetadataAccessorneeds it own test, similar to how BindingKey had its own test in that other PR
  • Couple packages had been missed out when the others' keys were converted to use MetadataAccessor
    • authentication
    • repository-json-schema (feel free to separate out the key to its own file for this one (didn't do this when I created the package :p))
    • there might be others, so I'd check other packages just to be sure
  • did you check for potential changes in the docs that would use this feature?

const TEST_META = MetadataAccessor.create<object, ClassDecorator>('test');

it('inspects metadata of a base class', () => {
const meta = MetadataInspector.getClassMetadata('test', BaseController);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't test being replaced with TEST_META here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to test string keys too.

const myClassDecorator = ClassDecoratorFactory.createDecorator(CLASS_KEY);

// Inspect a class with the key
const myClassMeta = MetadataInspector.getClassMetaData(CLASS_KEY, MyController);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we include a comment here insinuating that myClassMeta will have its type set to MyClassMetadata?

Copy link
Contributor

@shimks shimks left a comment

Choose a reason for hiding this comment

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

Also, make sure that the new file is mentioned in docs.json

@raymondfeng raymondfeng force-pushed the metadata-key branch 2 times, most recently from 4549518 to 8433547 Compare April 11, 2018 15:55
@raymondfeng
Copy link
Contributor Author

@shimks PTAL

import {MetadataInspector, MetadataAccessor} from '@loopback/context';

export const JSON_SCHEMA_KEY = 'loopback:json-schema';
export const JSON_SCHEMA_KEY = MetadataAccessor.create<JsonDefinition>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there no decorator type passed here in the <>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If missing, any decorator type is fine. There are valid cases that the same string is used for both class and member decorators.

Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't realize it still works with just key strings. :)

export class MetadataAccessor<T, D extends DecoratorType = DecoratorType> {
private constructor(public readonly key: string) {}

toString() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it cleaner and easier to access the .key property directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please note toString() works for both MetadataAccessor and string so that we don't to test if the key is a string or not.

options && options.ownMetadataOnly
? Reflector.getOwnMetadata(key, target)
: Reflector.getMetadata(key, target);
? Reflector.getOwnMetadata(key.toString(), target)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker but using .toString() is messy imo. Wouldn't it be better to do key.key or key.name with us storing the key as the name property?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See my explanation above.

Copy link
Contributor

@shimks shimks left a comment

Choose a reason for hiding this comment

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

LGTM, but please wait on merging this until @bajtos's review since the PR itself had been inspired by the PR he authored.

).to.equal('bar');
});

it('can be used to create decorator', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be nitpicky, but I think this test should live in a separate integration folder

import {BindingKey} from '@loopback/context';
import {AuthenticateFn, UserProfile} from './providers/authentication.provider';
import {AuthenticationMetadata} from './decorators/authenticate.decorator';
import {BindingKey, MetadataAccessor} from '@loopback/context';
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't MetadataAccessor be imported from @loopback/metadata? Actually I'm not even sure how this is working since I didn't see a re-export of MetadataAccessor from @loopback/context and this class is defined in @loopback/metadata.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

* @typeparam T Type of the metadata value
* @typeparam D Type of the decorator
*/
export class MetadataAccessor<T, D extends DecoratorType = DecoratorType> {
Copy link
Contributor

Choose a reason for hiding this comment

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

The class shouldn't be in a types.ts file. Should have it's own file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we move MetadataAccessor to its own file, it will create a circular deps:

  • MetadataAccessor --> DecoratorType (metadata-accessor.ts)
  • MetadataKey --> MetadataAccessor (types.ts)

BTW, a class plays dual roles of typing and implementing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I know a class serves as a type but just thought types.ts for was types / interfaces not classes.

Didn't see the circular dependency. Good enough reason to keep it here.

Copy link
Member

@bajtos bajtos left a comment

Choose a reason for hiding this comment

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

I quickly skimmed through the patch and don't see any major issues.

Is there any additional documentation (either as API docs or in /docs/site) that should be updated to reflect these changes?

@raymondfeng raymondfeng merged commit 45f9f80 into master Apr 12, 2018
@raymondfeng
Copy link
Contributor Author

Is there any additional documentation (either as API docs or in /docs/site) that should be updated to reflect these changes?

I have updated the README, which is the docs we pull into loopback.io for metadata.

@raymondfeng raymondfeng deleted the metadata-key branch April 12, 2018 15:40
@dhmlau dhmlau mentioned this pull request Apr 30, 2018
23 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants