Skip to content

Commit

Permalink
Use strategies to generate (or omit) the identifier property
Browse files Browse the repository at this point in the history
  • Loading branch information
bravo-kernel committed Aug 15, 2019
1 parent f4c997d commit 42d3038
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 37 deletions.
33 changes: 25 additions & 8 deletions lib/schema-manager.js
Expand Up @@ -20,18 +20,24 @@ const _options = new WeakMap();
*/
const _model = new WeakMap();

/**
* Passed schema strategy.
*
* @private
*/
const _strategy = new WeakMap();

/**
* SchemaManager defines the interface class we use to publish methods for our end-users.
*/
class SchemaManager {
/**
* @param {object} options
* @param {object} options User options.
* @param {StrategyInterface} options.strategy Strategy instance
* @param {string} options.base Base URI prefixed to generated URLs, defaults to '/'*
*/
constructor(options) {
const defaultOptions = {
strategy: null,
baseUri: '/',
};

Expand Down Expand Up @@ -73,6 +79,8 @@ class SchemaManager {
if (!(strategy instanceof StrategyInterface))
throw new TypeError("Strategy must implement the 'StrategyInterface'");

_strategy.set(this, strategy);

// extract sequelize properties
// const rawAttributes = this._getRawAttributes(model);

Expand Down Expand Up @@ -105,6 +113,7 @@ class SchemaManager {
_getUriForModel() {
const path = this._getFileNameForModel(_model.get(this));
const { baseUri } = _options.get(this).baseUri;

return `${baseUri}${path}`;
}

Expand All @@ -127,18 +136,26 @@ class SchemaManager {

/**
* Generates the expected boiler plate for a model schema
*
* @todo refactor into separate methods
* @return {Object} The boiler plate for a json schema for this model
*/
boilerPlate() {
const model = _model.get(this);
const title = _.capitalize(plural.singular(model.constructor.name));
const title = _.capitalize(plural.singular(_model.get(this).constructor.name));

return {
const result = {
title,
$id: this._getUriForModel(),
type: 'object',
$schema: 'http://json-schema.org/draft-06/schema#',
};

const id = _strategy.get(this).getIdentifierKeyword();
if (id) {
result[id] = this._getUriForModel();
}

result.type = 'object';
result['$schema'] = 'http://json-schema.org/draft-06/schema#'; // eslint-disable-line dot-notation

return result;
}
}

Expand Down
17 changes: 5 additions & 12 deletions lib/strategies/json-schema-v6.js
Expand Up @@ -29,21 +29,14 @@ class JsonSchema6Strategy extends StrategyInterface {
}

/**
* Returns the id property specific for this strategy.
* Returns the keyword used for the `id` property.
*
* @example
* {
* $id: '/properties/createdAt'
* }
* @example $id
*
* @param {string} baseUri Configuration option
* @param {string} propertyName Name of the Sequelize attribute
* @returns {object}
* @returns {string|null} Null will prevent the identifier from appearing in the schema (e.g. for OpenAPI)
*/
getIdProperty(baseUri, propertyName) {
return {
$id: `${baseUri}/properties/${propertyName}`,
};
getIdentifierKeyword() {
return '$id';
}

/**
Expand Down
9 changes: 4 additions & 5 deletions lib/strategies/openapi-v3.js
Expand Up @@ -29,14 +29,13 @@ class OpenApi3Strategy extends StrategyInterface {
}

/**
* Returns `null` because OpenAPI v3 does not support this property.
* OpenAPI v3 does not support/allow the `id` keyword so we return `null` so it will be omitted from the schema.
*
* @example null
*
* @param {string} baseUri Configuration option
* @param {string} propertyName Name of the Sequelize attribute
* @returns {null}
*/
// prettier-ignore
getIdProperty(baseUri, propertyName) { // eslint-disable-line no-unused-vars
getIdentifierKeyword() {
return null;
}

Expand Down
17 changes: 9 additions & 8 deletions lib/strategy-interface.js
Expand Up @@ -10,25 +10,26 @@ const _throwMissingImplementationError = Symbol('Private method');
*/
class StrategyInterface {
/**
* Must return the strategy specific 'example' property.
* Should returns the keyword used for the `id` schema property (if applicable)
*
* @param {string} raw Raw value as derived from the Sequelize model
* @returns {object|null}
* @see {@link https://json-schema.org/understanding-json-schema/structuring.html#the-id-property}
* @param {string} path Path
* @returns {string|null} Null will make the identifier property not appear in the schema
*/
// prettier-ignore
getExampleProperty(raw) { // eslint-disable-line no-unused-vars
this[_throwMissingImplementationError](this.constructor.name, 'getExampleProperty');
getIdentifierKeyword() {
this[_throwMissingImplementationError](this.constructor.name, 'getIdentifierKeyword');
}

/**
* Must return the strategy specific 'id' property.
* Must return the strategy specific 'example' property.
*
* @param {string} raw Raw value as derived from the Sequelize model
* @returns {object|null}
*/
// prettier-ignore
getIdProperty(baseUri, propertyName) { // eslint-disable-line no-unused-vars
this[_throwMissingImplementationError](this.constructor.name, 'getIdProperty');
getExampleProperty(raw) { // eslint-disable-line no-unused-vars
this[_throwMissingImplementationError](this.constructor.name, 'getExampleProperty');
}

/**
Expand Down
8 changes: 4 additions & 4 deletions test-the-strategy-pattern.js
@@ -1,5 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */

/**
* Test runner used for Rapid Development.
Expand All @@ -20,17 +21,16 @@ const userModel = sequelize.import("./test/models/user.js").build();
// ============================================================================
// Test the SchemaManager and strategies
// ============================================================================
const { SchemaManager, JsonSchema6Strategy } = require('./lib');
const { SchemaManager, JsonSchema6Strategy, OpenApi3Strategy } = require('./lib');

// Initialize the SchemaManager with non-strategy-specific options
const schemaManager = new SchemaManager({
baseUri: 'https://api.example.com',
});

// Initialize the JsonSchema6Strategy with strategy-specific options
const strategy = new JsonSchema6Strategy({
woot: 'whatever',
});
// const strategy = new OpenApi3Strategy();
const strategy = new JsonSchema6Strategy();

// Generate the schema
const result = schemaManager.generate(userModel, strategy);
Expand Down

0 comments on commit 42d3038

Please sign in to comment.