From c05477f7f3f3f05d14da2db98c068fa9ae683c84 Mon Sep 17 00:00:00 2001 From: bravo-kernel Date: Sat, 28 Nov 2020 18:33:39 +0100 Subject: [PATCH] Fix: now renders valid OAS3 for Sequelize BLOB DataType (#77) --- examples/json-schema-v7.md | 14 +++++++++++++- examples/openapi-v3.md | 12 +++++++++++- lib/strategies/json-schema-v7.js | 16 ++++++++++++++++ lib/strategies/openapi-v3.js | 16 ++++++++++++++++ lib/strategy-interface.js | 12 ++++++++++++ lib/type-mapper.js | 3 ++- test/models/user.js | 7 +++++++ test/strategies/json-schema-v7-strategy.test.js | 6 ++++++ test/strategies/openapi-v3-stragegy.test.js | 6 ++++++ test/strategy-interface.test.js | 12 ++++++++++-- 10 files changed, 99 insertions(+), 5 deletions(-) diff --git a/examples/json-schema-v7.md b/examples/json-schema-v7.md index 8ae617b..108d693 100644 --- a/examples/json-schema-v7.md +++ b/examples/json-schema-v7.md @@ -1,6 +1,6 @@ # JSON Schema Draft-07 -These schemas were automatically generated on 2020-01-25 +These schemas were automatically generated on 2020-11-28 using [these Sequelize models](../test/models) and the most recent version of sequelize-to-json-schemas. To confirm that these are indeed all valid schemas use: @@ -68,6 +68,11 @@ sequelize-to-json-schemas. To confirm that these are indeed all valid schemas us "type": "string" } }, + "BLOB": { + "$id": "https://api.example.com/properties/BLOB", + "type": "string", + "contentEncoding": "base64" + }, "CITEXT": { "$id": "https://api.example.com/properties/CITEXT", "type": "string" @@ -259,6 +264,7 @@ sequelize-to-json-schemas. To confirm that these are indeed all valid schemas us "updatedAt", "ARRAY_INTEGERS", "ARRAY_TEXTS", + "BLOB", "CITEXT", "INTEGER", "STRING", @@ -495,6 +501,11 @@ document (by adding model schemas to `definitions`). "type": "string" } }, + "BLOB": { + "$id": "https://api.example.com/properties/BLOB", + "type": "string", + "contentEncoding": "base64" + }, "CITEXT": { "$id": "https://api.example.com/properties/CITEXT", "type": "string" @@ -686,6 +697,7 @@ document (by adding model schemas to `definitions`). "updatedAt", "ARRAY_INTEGERS", "ARRAY_TEXTS", + "BLOB", "CITEXT", "INTEGER", "STRING", diff --git a/examples/openapi-v3.md b/examples/openapi-v3.md index 11e0380..a3898c6 100644 --- a/examples/openapi-v3.md +++ b/examples/openapi-v3.md @@ -1,6 +1,6 @@ # OpenAPI 3.0 -These schemas were automatically generated on 2020-01-25 +These schemas were automatically generated on 2020-11-28 using [these Sequelize models](../test/models) and the most recent version of sequelize-to-json-schemas. To confirm that these are indeed all valid schemas use: @@ -56,6 +56,10 @@ sequelize-to-json-schemas. To confirm that these are indeed all valid schemas us }, "nullable": true }, + "BLOB": { + "type": "string", + "format": "byte" + }, "CITEXT": { "type": "string" }, @@ -216,6 +220,7 @@ sequelize-to-json-schemas. To confirm that these are indeed all valid schemas us "updatedAt", "ARRAY_INTEGERS", "ARRAY_TEXTS", + "BLOB", "CITEXT", "INTEGER", "STRING", @@ -421,6 +426,10 @@ example of how to integrate the generated model schemas into a full OpenAPI 3.0 }, "nullable": true }, + "BLOB": { + "type": "string", + "format": "byte" + }, "CITEXT": { "type": "string" }, @@ -581,6 +590,7 @@ example of how to integrate the generated model schemas into a full OpenAPI 3.0 "updatedAt", "ARRAY_INTEGERS", "ARRAY_TEXTS", + "BLOB", "CITEXT", "INTEGER", "STRING", diff --git a/lib/strategies/json-schema-v7.js b/lib/strategies/json-schema-v7.js index 6861e6a..dcc7e83 100644 --- a/lib/strategies/json-schema-v7.js +++ b/lib/strategies/json-schema-v7.js @@ -99,6 +99,22 @@ class JsonSchema7Strategy extends StrategyInterface { }; } + /** + * Returns the `contentEncoding` property as used by Json Schema for base64 encoded strings (like BLOB). + * + * @example + * { + * 'contentEncoding': 'base64', + * } + * + * @returns {object} + */ + getPropertyForBase64Encoding() { + return { + contentEncoding: 'base64', + }; + } + /** * Returns the property pointing to a HasOne association. * diff --git a/lib/strategies/openapi-v3.js b/lib/strategies/openapi-v3.js index 94220f9..d9b8100 100644 --- a/lib/strategies/openapi-v3.js +++ b/lib/strategies/openapi-v3.js @@ -61,6 +61,22 @@ class OpenApi3Strategy extends StrategyInterface { }; } + /** + * Returns the `format` property as used by OAS for base64 base64 encoded strings (like BLOB). + * + * @example + * { + * 'format': 'byte', + * } + * + * @returns {object} + */ + getPropertyForBase64Encoding() { + return { + format: 'byte', + }; + } + /** * Returns a new `type` property, enriched to allow null values. * diff --git a/lib/strategy-interface.js b/lib/strategy-interface.js index 898edc2..d307041 100644 --- a/lib/strategy-interface.js +++ b/lib/strategy-interface.js @@ -54,6 +54,18 @@ class StrategyInterface { this.constructor._throwMissingImplementationError(this.constructor.name, 'getPropertyExamples'); } + /** + * Must return the strategy specific property used for base64 string encoding. + * + * @returns {object} + */ + getPropertyForBase64Encoding() { + this.constructor._throwMissingImplementationError( + this.constructor.name, + 'getPropertyForBase64Encoding', + ); + } + /** * Must returns a new `type` object, enriched to allow null values. * diff --git a/lib/type-mapper.js b/lib/type-mapper.js index 7c1c457..b36119f 100644 --- a/lib/type-mapper.js +++ b/lib/type-mapper.js @@ -76,7 +76,8 @@ class TypeMapper { // @todo BLOB('tiny') // ---------------------------------------------------------------------- case 'BLOB': { - result = { ...STRING, contentEncoding: 'base64' }; + result = { ...STRING }; + Object.assign(result, strategy.getPropertyForBase64Encoding()); break; } diff --git a/test/models/user.js b/test/models/user.js index 7781583..7ff5885 100644 --- a/test/models/user.js +++ b/test/models/user.js @@ -57,6 +57,13 @@ module.exports = (sequelize, { DataTypes }) => { }; } + if (supportedDataType('BLOB')) { + Model.rawAttributes.BLOB = { + type: DataTypes.BLOB, + allowNull: false, + }; + } + if (supportedDataType('CITEXT')) { Model.rawAttributes.CITEXT = { type: DataTypes.CITEXT, diff --git a/test/strategies/json-schema-v7-strategy.test.js b/test/strategies/json-schema-v7-strategy.test.js index b8d56b4..4d81485 100644 --- a/test/strategies/json-schema-v7-strategy.test.js +++ b/test/strategies/json-schema-v7-strategy.test.js @@ -40,6 +40,12 @@ describe('JsonSchema7Strategy', function () { // make sure sequelize DataTypes render as expected // ------------------------------------------------------------------------ describe('Ensure Sequelize DataTypes are properly converted and thus:', function () { + describe('BLOB', function () { + it("has property 'contentEncoding' of type 'base64'", function () { + expect(schema.properties.BLOB.contentEncoding).toEqual('base64'); + }); + }); + describe('STRING_ALLOWNULL_EXPLICIT', function () { it("has property 'type' of type 'array'", function () { expect(Array.isArray(schema.properties.STRING_ALLOWNULL_EXPLICIT.type)).toBe(true); diff --git a/test/strategies/openapi-v3-stragegy.test.js b/test/strategies/openapi-v3-stragegy.test.js index f65ba0e..30c3a40 100644 --- a/test/strategies/openapi-v3-stragegy.test.js +++ b/test/strategies/openapi-v3-stragegy.test.js @@ -26,6 +26,12 @@ describe('OpenApi3Strategy', function () { // make sure sequelize DataTypes render as expected // ------------------------------------------------------------------------ describe('Ensure Sequelize DataTypes are properly converted and thus:', function () { + describe('BLOB', function () { + it("has property 'format' of type 'byte'", function () { + expect(schema.properties.BLOB.format).toEqual('byte'); + }); + }); + describe('STRING_ALLOWNULL_EXPLICIT', function () { it("has property 'type' of type 'string'", function () { expect(schema.properties.STRING_ALLOWNULL_EXPLICIT.type).toEqual('string'); diff --git a/test/strategy-interface.test.js b/test/strategy-interface.test.js index fdf0941..3b4d0b5 100644 --- a/test/strategy-interface.test.js +++ b/test/strategy-interface.test.js @@ -17,8 +17,8 @@ describe('StrategyInterface', function () { describe('Ensure we are testing against:', function () { const methodCount = Object.getOwnPropertyNames(StrategyInterface.prototype).length - 1; // excluding the constructor - it(`7 interface methods`, function () { - expect(methodCount).toEqual(7); + it(`8 interface methods`, function () { + expect(methodCount).toEqual(8); }); }); @@ -56,6 +56,14 @@ describe('StrategyInterface', function () { }).toThrow("DummyStrategy has not implemented the 'getPropertyExamples' interface method."); }); + it('getPropertyForBase64Encoding()', function () { + expect(() => { + dummyStrategy.getPropertyForBase64Encoding(); + }).toThrow( + "DummyStrategy has not implemented the 'getPropertyForBase64Encoding' interface method.", + ); + }); + it('convertTypePropertyToAllowNull()', function () { expect(() => { dummyStrategy.convertTypePropertyToAllowNull();