@kravc/schema 2.7.6 → 2.8.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +19 -14
  2. package/dist/CredentialFactory.d.ts +345 -0
  3. package/dist/CredentialFactory.d.ts.map +1 -0
  4. package/dist/CredentialFactory.js +381 -0
  5. package/dist/CredentialFactory.js.map +1 -0
  6. package/dist/Schema.d.ts +448 -0
  7. package/dist/Schema.d.ts.map +1 -0
  8. package/dist/Schema.js +506 -0
  9. package/dist/Schema.js.map +1 -0
  10. package/dist/ValidationError.d.ts +70 -0
  11. package/dist/ValidationError.d.ts.map +1 -0
  12. package/dist/ValidationError.js +78 -0
  13. package/dist/ValidationError.js.map +1 -0
  14. package/dist/Validator.d.ts +483 -0
  15. package/dist/Validator.d.ts.map +1 -0
  16. package/dist/Validator.js +570 -0
  17. package/dist/Validator.js.map +1 -0
  18. package/dist/helpers/JsonSchema.d.ts +99 -0
  19. package/dist/helpers/JsonSchema.d.ts.map +1 -0
  20. package/dist/helpers/JsonSchema.js +3 -0
  21. package/dist/helpers/JsonSchema.js.map +1 -0
  22. package/dist/helpers/cleanupAttributes.d.ts +34 -0
  23. package/dist/helpers/cleanupAttributes.d.ts.map +1 -0
  24. package/dist/helpers/cleanupAttributes.js +113 -0
  25. package/dist/helpers/cleanupAttributes.js.map +1 -0
  26. package/dist/helpers/cleanupNulls.d.ts +27 -0
  27. package/dist/helpers/cleanupNulls.d.ts.map +1 -0
  28. package/dist/helpers/cleanupNulls.js +96 -0
  29. package/dist/helpers/cleanupNulls.js.map +1 -0
  30. package/dist/helpers/createSchemasMap.d.ts +67 -0
  31. package/dist/helpers/createSchemasMap.d.ts.map +1 -0
  32. package/dist/helpers/createSchemasMap.js +200 -0
  33. package/dist/helpers/createSchemasMap.js.map +1 -0
  34. package/dist/helpers/getReferenceIds.d.ts +169 -0
  35. package/dist/helpers/getReferenceIds.d.ts.map +1 -0
  36. package/dist/helpers/getReferenceIds.js +241 -0
  37. package/dist/helpers/getReferenceIds.js.map +1 -0
  38. package/dist/helpers/got.d.ts +60 -0
  39. package/dist/helpers/got.d.ts.map +1 -0
  40. package/dist/helpers/got.js +72 -0
  41. package/dist/helpers/got.js.map +1 -0
  42. package/dist/helpers/mapObjectProperties.d.ts +150 -0
  43. package/dist/helpers/mapObjectProperties.d.ts.map +1 -0
  44. package/dist/helpers/mapObjectProperties.js +229 -0
  45. package/dist/helpers/mapObjectProperties.js.map +1 -0
  46. package/dist/helpers/normalizeAttributes.d.ts +213 -0
  47. package/dist/helpers/normalizeAttributes.d.ts.map +1 -0
  48. package/dist/helpers/normalizeAttributes.js +243 -0
  49. package/dist/helpers/normalizeAttributes.js.map +1 -0
  50. package/dist/helpers/normalizeProperties.d.ts +168 -0
  51. package/dist/helpers/normalizeProperties.d.ts.map +1 -0
  52. package/dist/helpers/normalizeProperties.js +223 -0
  53. package/dist/helpers/normalizeProperties.js.map +1 -0
  54. package/dist/helpers/normalizeRequired.d.ts +159 -0
  55. package/dist/helpers/normalizeRequired.d.ts.map +1 -0
  56. package/dist/helpers/normalizeRequired.js +206 -0
  57. package/dist/helpers/normalizeRequired.js.map +1 -0
  58. package/dist/helpers/normalizeType.d.ts +81 -0
  59. package/dist/helpers/normalizeType.d.ts.map +1 -0
  60. package/dist/helpers/normalizeType.js +210 -0
  61. package/dist/helpers/normalizeType.js.map +1 -0
  62. package/dist/helpers/nullifyEmptyValues.d.ts +139 -0
  63. package/dist/helpers/nullifyEmptyValues.d.ts.map +1 -0
  64. package/dist/helpers/nullifyEmptyValues.js +191 -0
  65. package/dist/helpers/nullifyEmptyValues.js.map +1 -0
  66. package/dist/helpers/removeRequiredAndDefault.d.ts +106 -0
  67. package/dist/helpers/removeRequiredAndDefault.d.ts.map +1 -0
  68. package/dist/helpers/removeRequiredAndDefault.js +138 -0
  69. package/dist/helpers/removeRequiredAndDefault.js.map +1 -0
  70. package/dist/helpers/validateId.d.ts +39 -0
  71. package/dist/helpers/validateId.d.ts.map +1 -0
  72. package/dist/helpers/validateId.js +51 -0
  73. package/dist/helpers/validateId.js.map +1 -0
  74. package/dist/index.d.ts +9 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +21 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/ld/documentLoader.d.ts +8 -0
  79. package/dist/ld/documentLoader.d.ts.map +1 -0
  80. package/dist/ld/documentLoader.js +24 -0
  81. package/dist/ld/documentLoader.js.map +1 -0
  82. package/dist/ld/getLinkedDataAttributeType.d.ts +10 -0
  83. package/dist/ld/getLinkedDataAttributeType.d.ts.map +1 -0
  84. package/dist/ld/getLinkedDataAttributeType.js +32 -0
  85. package/dist/ld/getLinkedDataAttributeType.js.map +1 -0
  86. package/dist/ld/getLinkedDataContext.d.ts +19 -0
  87. package/dist/ld/getLinkedDataContext.d.ts.map +1 -0
  88. package/dist/ld/getLinkedDataContext.js +50 -0
  89. package/dist/ld/getLinkedDataContext.js.map +1 -0
  90. package/eslint.config.mjs +32 -52
  91. package/examples/credentials/createAccountCredential.ts +27 -0
  92. package/examples/credentials/createMineSweeperScoreCredential.ts +115 -0
  93. package/examples/index.ts +7 -0
  94. package/examples/schemas/FavoriteItemSchema.ts +27 -0
  95. package/examples/{Preferences.yaml → schemas/Preferences.yaml} +2 -0
  96. package/examples/schemas/PreferencesSchema.ts +29 -0
  97. package/examples/schemas/ProfileSchema.ts +91 -0
  98. package/examples/schemas/Status.yaml +3 -0
  99. package/examples/schemas/StatusSchema.ts +12 -0
  100. package/jest.config.mjs +5 -0
  101. package/package.json +27 -20
  102. package/src/CredentialFactory.ts +392 -0
  103. package/src/Schema.ts +583 -0
  104. package/src/ValidationError.ts +90 -0
  105. package/src/Validator.ts +603 -0
  106. package/src/__tests__/CredentialFactory.test.ts +588 -0
  107. package/src/__tests__/Schema.test.ts +371 -0
  108. package/src/__tests__/ValidationError.test.ts +235 -0
  109. package/src/__tests__/Validator.test.ts +787 -0
  110. package/src/helpers/JsonSchema.ts +119 -0
  111. package/src/helpers/__tests__/cleanupAttributes.test.ts +943 -0
  112. package/src/helpers/__tests__/cleanupNulls.test.ts +772 -0
  113. package/src/helpers/__tests__/createSchemasMap.test.ts +238 -0
  114. package/src/helpers/__tests__/getReferenceIds.test.ts +975 -0
  115. package/src/helpers/__tests__/got.test.ts +193 -0
  116. package/src/helpers/__tests__/mapObjectProperties.test.ts +1126 -0
  117. package/src/helpers/__tests__/normalizeAttributes.test.ts +1435 -0
  118. package/src/helpers/__tests__/normalizeProperties.test.ts +727 -0
  119. package/src/helpers/__tests__/normalizeRequired.test.ts +669 -0
  120. package/src/helpers/__tests__/normalizeType.test.ts +772 -0
  121. package/src/helpers/__tests__/nullifyEmptyValues.test.ts +735 -0
  122. package/src/helpers/__tests__/removeRequiredAndDefault.test.ts +734 -0
  123. package/src/helpers/__tests__/validateId.test.ts +118 -0
  124. package/src/helpers/cleanupAttributes.ts +151 -0
  125. package/src/helpers/cleanupNulls.ts +106 -0
  126. package/src/helpers/createSchemasMap.ts +212 -0
  127. package/src/helpers/getReferenceIds.ts +273 -0
  128. package/src/helpers/got.ts +73 -0
  129. package/src/helpers/mapObjectProperties.ts +272 -0
  130. package/src/helpers/normalizeAttributes.ts +247 -0
  131. package/src/helpers/normalizeProperties.ts +249 -0
  132. package/src/helpers/normalizeRequired.ts +233 -0
  133. package/src/helpers/normalizeType.ts +235 -0
  134. package/src/helpers/nullifyEmptyValues.ts +207 -0
  135. package/src/helpers/removeRequiredAndDefault.ts +151 -0
  136. package/src/helpers/validateId.ts +53 -0
  137. package/src/index.ts +17 -0
  138. package/src/ld/__tests__/documentLoader.test.ts +57 -0
  139. package/src/ld/__tests__/getLinkedDataAttributeType.test.ts +212 -0
  140. package/src/ld/__tests__/getLinkedDataContext.test.ts +378 -0
  141. package/src/ld/documentLoader.ts +28 -0
  142. package/src/ld/getLinkedDataAttributeType.ts +46 -0
  143. package/src/ld/getLinkedDataContext.ts +80 -0
  144. package/tsconfig.json +27 -0
  145. package/types/credentials-context.d.ts +14 -0
  146. package/types/security-context.d.ts +6 -0
  147. package/examples/Status.yaml +0 -3
  148. package/examples/createAccountCredential.js +0 -27
  149. package/examples/createMineSweeperScoreCredential.js +0 -63
  150. package/examples/index.js +0 -9
  151. package/src/CredentialFactory.js +0 -67
  152. package/src/CredentialFactory.spec.js +0 -131
  153. package/src/Schema.js +0 -104
  154. package/src/Schema.spec.js +0 -172
  155. package/src/ValidationError.js +0 -31
  156. package/src/Validator.js +0 -128
  157. package/src/Validator.spec.js +0 -355
  158. package/src/helpers/cleanupAttributes.js +0 -71
  159. package/src/helpers/cleanupNulls.js +0 -42
  160. package/src/helpers/getReferenceIds.js +0 -71
  161. package/src/helpers/mapObject.js +0 -65
  162. package/src/helpers/normalizeAttributes.js +0 -28
  163. package/src/helpers/normalizeProperties.js +0 -61
  164. package/src/helpers/normalizeRequired.js +0 -37
  165. package/src/helpers/normalizeType.js +0 -41
  166. package/src/helpers/nullifyEmptyValues.js +0 -57
  167. package/src/helpers/removeRequiredAndDefault.js +0 -30
  168. package/src/helpers/validateId.js +0 -19
  169. package/src/index.d.ts +0 -25
  170. package/src/index.js +0 -8
  171. package/src/ld/documentLoader.js +0 -25
  172. package/src/ld/documentLoader.spec.js +0 -12
  173. package/src/ld/getLinkedDataContext.js +0 -63
  174. package/src/ld/getLinkedDataType.js +0 -38
  175. /package/examples/{FavoriteItem.yaml → schemas/FavoriteItem.yaml} +0 -0
  176. /package/examples/{Profile.yaml → schemas/Profile.yaml} +0 -0
@@ -1,67 +0,0 @@
1
- 'use strict'
2
-
3
- const Schema = require('./Schema')
4
- const Validator = require('./Validator')
5
- const validateId = require('./helpers/validateId')
6
- const { constants } = require('credentials-context')
7
-
8
- const { CREDENTIALS_CONTEXT_V1_URL } = constants
9
-
10
- class CredentialFactory {
11
- // TODO: Add exception when added two schemas with the same name.
12
- constructor(uri, schemas) {
13
- validateId('uri', uri)
14
-
15
- this._types = [].concat(schemas).map(schema => {
16
- if (schema.url) {
17
- return schema
18
- }
19
-
20
- return new Schema(schema, schema.id, uri)
21
- })
22
-
23
- this._uri = uri
24
- this._context = {}
25
- this._validator = new Validator(this._types)
26
-
27
- for (const { id, linkedDataType } of this._types) {
28
- this._context[id] = linkedDataType
29
- }
30
- }
31
-
32
- get credentialType() {
33
- const [ credentialType ] = this._uri.split('/').reverse()
34
-
35
- return credentialType
36
- }
37
-
38
- get context() {
39
- return {
40
- [this.credentialType]: { '@id': this._uri },
41
- ...this._context
42
- }
43
- }
44
-
45
- createCredential(id, holder, subject = {}) {
46
- validateId('id', id)
47
- validateId('holder', holder)
48
-
49
- const type = [ 'VerifiableCredential', this.credentialType ]
50
-
51
- const [ rootType ] = this._types
52
- const credentialSubject = this._validator.validate(subject, rootType.id)
53
-
54
- return {
55
- '@context': [
56
- CREDENTIALS_CONTEXT_V1_URL,
57
- this.context
58
- ],
59
- id,
60
- type,
61
- holder,
62
- credentialSubject
63
- }
64
- }
65
- }
66
-
67
- module.exports = CredentialFactory
@@ -1,131 +0,0 @@
1
- 'use strict'
2
-
3
- const Schema = require('./Schema')
4
- const { expect } = require('chai')
5
- const { canonize } = require('jsonld')
6
- const documentLoader = require('./ld/documentLoader')
7
- const CredentialFactory = require('./CredentialFactory')
8
- const {
9
- createAccountCredential,
10
- createMineSweeperScoreCredential
11
- } = require('examples')
12
-
13
- describe('CredentialFactory', () => {
14
- describe('CredentialFactory.constructor(uri, schemas)', () => {
15
- it('throws error if "uri" parameter is missing', () => {
16
- expect(
17
- () => new CredentialFactory()
18
- ).to.throw()
19
- })
20
-
21
- it('throws error if "uri" parameter is not a URL', () => {
22
- expect(
23
- () => new CredentialFactory('BAD_URL')
24
- ).to.throw()
25
- })
26
- })
27
-
28
- describe('.createCredential(id, holder, subject = {}, subjectTypeId = null)', () => {
29
- let factory
30
-
31
- before(() => {
32
- const videoGameSchema = new Schema({
33
- id: {},
34
- name: { type: 'string', required: true },
35
- version: { type: 'string', required: true }
36
- }, 'VideoGame', 'https://schema.org/')
37
-
38
- factory = new CredentialFactory('https://example.com/StarCraft', videoGameSchema)
39
- })
40
-
41
- it('returns single schema based credential', async () => {
42
- const credential = await createAccountCredential('did:PLAYER_ID', 'CAHTEP')
43
-
44
- expect(credential).to.exist
45
- await canonize(credential, { documentLoader })
46
-
47
- const { credentialSubject: { createdAt } } = credential
48
- expect(credential).to.eql({
49
- '@context': [
50
- 'https://www.w3.org/2018/credentials/v1',
51
- {
52
- AccountV1: { '@id': 'https://example.com/schema/AccountV1' },
53
- Account: {
54
- '@id': 'https://example.com/schema/AccountV1#Account',
55
- '@context': {
56
- '@vocab': 'https://example.com/schema/AccountV1#',
57
- '@version': 1.1,
58
- '@protected': true,
59
- schema: 'https://schema.org/',
60
- username: { '@id': 'username' },
61
- createdAt: { '@id': 'createdAt', '@type': 'schema:DateTime' },
62
- dateOfBirth: { '@id': 'dateOfBirth', '@type': 'schema:Date' }
63
- }
64
- }
65
- }
66
- ],
67
- id: 'https://example.com/account/CAHTEP',
68
- type: [ 'VerifiableCredential', 'AccountV1' ],
69
- holder: 'did:PLAYER_ID',
70
- credentialSubject: {
71
- id: 'did:PLAYER_ID',
72
- username: 'CAHTEP',
73
- createdAt,
74
- type: 'Account'
75
- }
76
- })
77
- })
78
-
79
- it('returns multiple schemas based credential', async () => {
80
- const playerScore = {
81
- wins: 5,
82
- losses: 5,
83
- winRate: 50,
84
- UNDEFINED: 'VALUE',
85
- bestScore: 23450,
86
- endurance: 'P5M22S',
87
- dateCreated: '2020-10-10T00:00:00Z',
88
- bestRoundTime: 10000
89
- }
90
-
91
- const credential = await createMineSweeperScoreCredential(
92
- 'did:GAME_ID',
93
- 'did:PLAYER_ID',
94
- playerScore
95
- )
96
-
97
- expect(credential).to.exist
98
- await canonize(credential, { documentLoader })
99
-
100
- const { credentialSubject } = credential
101
- expect(credentialSubject.type).to.equal('Player')
102
- expect(credentialSubject.hasVideoGameScore.type).to.equal('VideoGameScore')
103
- expect(credentialSubject.hasVideoGameScore.game.type).to.equal('VideoGame')
104
- expect(credentialSubject.hasVideoGameScore.UNDEFINED).to.not.exist
105
-
106
- const customContext = credential['@context'][1]
107
- expect(customContext.VideoGame).to.eql({
108
- '@id': 'https://schema.org/VideoGame',
109
- '@context': {
110
- '@protected': true,
111
- '@version': 1.1,
112
- '@vocab': 'https://schema.org/',
113
- name: { '@id': 'name' },
114
- version: { '@id': 'version' }
115
- }
116
- })
117
- })
118
-
119
- it('throws error if "id" parameter is missing', () => {
120
- expect(
121
- () => factory.createCredential()
122
- ).to.throw('Parameter "id" is required')
123
- })
124
-
125
- it('throws error if "holder" parameter is missing', () => {
126
- expect(
127
- () => factory.createCredential('did:CREDENTIAL_ID')
128
- ).to.throw('Parameter "holder" is required')
129
- })
130
- })
131
- })
package/src/Schema.js DELETED
@@ -1,104 +0,0 @@
1
- 'use strict'
2
-
3
- const { pick, cloneDeep } = require('lodash')
4
- const validateId = require('./helpers/validateId')
5
- const normalizeRequired = require('./helpers/normalizeRequired')
6
- const normalizeProperties = require('./helpers/normalizeProperties')
7
- const getLinkedDataContext = require('./ld/getLinkedDataContext')
8
- const removeRequiredAndDefault = require('./helpers/removeRequiredAndDefault')
9
-
10
- const UNDEFINED_SCHEMA_ID = 'UNDEFINED_SCHEMA_ID'
11
-
12
- class Schema {
13
- constructor(source = {}, id = UNDEFINED_SCHEMA_ID, url) {
14
- this._id = id
15
- this._url = url
16
- this._source = source instanceof Schema ? source.source : source
17
-
18
- if (url) {
19
- validateId('url', url)
20
-
21
- this._source.type = { required: true, type: 'string', default: id }
22
-
23
- if (this._source.id) {
24
- this._source.id = { required: true, type: 'string', format: 'url' }
25
- }
26
-
27
- const uri =
28
- (url.endsWith('/') || url.endsWith('#')) ? `${url}${id}` : `${url}#${id}`
29
-
30
- this._linkedDataType = {
31
- '@id': uri,
32
- '@context': getLinkedDataContext(this._source, url)
33
- }
34
- }
35
-
36
- normalizeProperties(this._source)
37
- }
38
-
39
- get id() {
40
- return this._id
41
- }
42
-
43
- get url() {
44
- return this._url
45
- }
46
-
47
- get source() {
48
- return cloneDeep(this._source)
49
- }
50
-
51
- get jsonSchema() {
52
- if (this._source.enum) {
53
- return {
54
- id: this._id,
55
- ...this.source
56
- }
57
- }
58
-
59
- const jsonSchema = {
60
- id: this._id,
61
- type: 'object',
62
- properties: this.source
63
- }
64
-
65
- normalizeRequired(jsonSchema)
66
-
67
- return jsonSchema
68
- }
69
-
70
- get linkedDataType() {
71
- return this._linkedDataType
72
- }
73
-
74
- clone(id) {
75
- return new Schema(this.source, id)
76
- }
77
-
78
- pure(id) {
79
- const { properties: source } = removeRequiredAndDefault({ properties: this.source })
80
- return new Schema(source, id)
81
- }
82
-
83
- only(propertyNames, id) {
84
- const source = pick(this.source, propertyNames)
85
- return new Schema(source, id)
86
- }
87
-
88
- extend(properties, id) {
89
- return new Schema({ ...this.source, ...properties }, id)
90
- }
91
-
92
- wrap(propertyName, options = { required: true }, id) {
93
- const source = {
94
- [propertyName]: {
95
- properties: this.source,
96
- ...options
97
- }
98
- }
99
-
100
- return new Schema(source, id)
101
- }
102
- }
103
-
104
- module.exports = Schema
@@ -1,172 +0,0 @@
1
- 'use strict'
2
-
3
- const { load } = require('js-yaml')
4
- const { Schema } = require('src')
5
- const { expect } = require('chai')
6
- const { readFileSync } = require('fs')
7
-
8
- const loadSync = (yamlPath) => {
9
- const id = yamlPath.split('.')[0].split('/').reverse()[0]
10
- const source = load(readFileSync(yamlPath))
11
-
12
- return new Schema(source, id)
13
- }
14
-
15
- describe('Schema', () => {
16
- describe('Schema.constructor(source = {}, id = UNDEFINED_SCHEMA_ID)', () => {
17
- it('creates empty schema with default id', () => {
18
- const schema = new Schema()
19
-
20
- expect(schema.id).to.equal('UNDEFINED_SCHEMA_ID')
21
- expect(schema.source).to.be.empty
22
- })
23
-
24
- it('extends schema properties with types', () => {
25
- const schema = loadSync('examples/Profile.yaml')
26
-
27
- expect(schema.source.name.type).to.eql('string')
28
- expect(schema.source.gender.type).to.eql('string')
29
- expect(schema.source.tags.items.type).to.eql('string')
30
- expect(schema.source.favoriteItems.type).to.eql('array')
31
- expect(schema.source.favoriteItems.items.type).to.not.exist
32
- expect(schema.source.locations.type).to.eql('array')
33
- expect(schema.source.locations.items.type).to.eql('object')
34
- expect(schema.source.locations.items.properties.name.type).to.eql('string')
35
- expect(schema.source.locations.items.properties.address.type).to.eql('object')
36
- expect(schema.source.locations.items.properties.address.properties.zip.type).to.eql('string')
37
- expect(schema.source.locations.items.properties.address.properties.city.type).to.eql('string')
38
- expect(schema.source.locations.items.properties.address.properties.country.type).to.eql('string')
39
- expect(schema.source.locations.items.properties.address.properties.addressLine1.type).to.eql('string')
40
- expect(schema.source.locations.items.properties.address.properties.addressLine2.type).to.eql('string')
41
-
42
- const stringEnumSchema = new Schema({ enum: [ 'L', 'M', 'S'] }, 'Size')
43
-
44
- expect(stringEnumSchema.source.type).to.eql('string')
45
-
46
- const numbersEnumSchema = new Schema({ enum: [ 1, 2, 3 ], type: 'number' }, 'Points')
47
-
48
- expect(numbersEnumSchema.source.type).to.eql('number')
49
- })
50
-
51
- it('creates schema from other schemas source', () => {
52
- const entitySchema = new Schema({ name: { type: 'string' } }, 'Entity')
53
-
54
- const schema = new Schema(entitySchema, 'EntityClone')
55
-
56
- expect(schema.id).to.equal('EntityClone')
57
- expect(schema.source).to.deep.equal(entitySchema.source)
58
- })
59
- })
60
-
61
- describe('.pure(id)', () => {
62
- it('returns schema without required and default attributes', () => {
63
- const profileSchema = loadSync('examples/Profile.yaml')
64
- const updateProfileSchema = profileSchema.pure('UpdateProfile')
65
-
66
- expect(updateProfileSchema.id).to.eql('UpdateProfile')
67
-
68
- const { source } = updateProfileSchema
69
-
70
- expect(source.name.required).to.not.exist
71
- expect(source.gender.default).to.not.exist
72
- expect(source.contactDetails.required).to.not.exist
73
- expect(source.contactDetails.properties.email.required).to.not.exist
74
- expect(source.contactDetails.properties.mobileNumber.default).to.not.exist
75
- expect(source.locations.items.properties.name.require).to.not.exist
76
- expect(source.locations.items.properties.address.properties.country.required).to.not.exist
77
- expect(source.locations.items.properties.address.properties.country.default).to.not.exist
78
- expect(source.locations.items.properties.address.properties.city.required).to.not.exist
79
- expect(source.locations.items.properties.address.properties.addressLine1.required).to.not.exist
80
- expect(source.locations.items.properties.address.properties.addressLine2.required).to.not.exist
81
- expect(source.locations.items.properties.address.properties.zip.required).to.not.exist
82
- })
83
- })
84
-
85
- describe('.clone(id)', () => {
86
- it('returns schema clone', () => {
87
- const profileSchema = loadSync('examples/Profile.yaml')
88
-
89
- const schema = profileSchema.clone('ProfileClone')
90
- expect(schema.id).to.eql('ProfileClone')
91
- })
92
- })
93
-
94
- describe('.only(propertyNames, id)', () => {
95
- it('returns schema with only requested properties', () => {
96
- const profileSchema = loadSync('examples/Profile.yaml')
97
-
98
- const schema = profileSchema.only([ 'name', 'gender' ], 'ProfileClone')
99
- expect(schema.id).to.eql('ProfileClone')
100
- })
101
- })
102
-
103
- describe('.extend(properties, id)', () => {
104
- it('returns schema extended with specified properties', () => {
105
- const profileSchema = loadSync('examples/Profile.yaml')
106
-
107
- const documentSource = {
108
- createdAt: {
109
- type: 'string',
110
- format: 'date-time',
111
- required: true
112
- }
113
- }
114
-
115
- const profileDocumentSchema = profileSchema.extend(documentSource, 'ProfileDocument')
116
-
117
- expect(profileDocumentSchema.id).to.eql('ProfileDocument')
118
- expect(profileDocumentSchema.source.createdAt).to.exist
119
- })
120
- })
121
-
122
- describe('.wrap(propertyName, options = { required: true }, id)', () => {
123
- it('returns schema that wraps source schema with object property', () => {
124
- const profileSchema = loadSync('examples/Profile.yaml')
125
-
126
- const dataSchema = profileSchema.wrap('data')
127
- expect(dataSchema.id).to.eql('UNDEFINED_SCHEMA_ID')
128
- expect(dataSchema.source.data).to.exist
129
- expect(dataSchema.source.data.required).to.exist
130
-
131
- const alternativeDataSchema = profileSchema.wrap('data', { default: {} }, 'ResponseOutput')
132
- expect(alternativeDataSchema.id).to.eql('ResponseOutput')
133
- expect(alternativeDataSchema.source.data).to.exist
134
- expect(alternativeDataSchema.source.data.default).to.exist
135
- expect(alternativeDataSchema.source.data.required).to.not.exist
136
- })
137
- })
138
-
139
- describe('.jsonSchema', () => {
140
- it('returns json schema for enum type', () => {
141
- const source = {
142
- type: 'string',
143
- enum: [ 'L', 'M', 'S' ]
144
- }
145
-
146
- const sizeSchema = new Schema(source, 'Size')
147
-
148
- expect(sizeSchema.jsonSchema.id).to.eql('Size')
149
- expect(sizeSchema.jsonSchema.type).to.eql('string')
150
- expect(sizeSchema.jsonSchema.enum).to.deep.equal(source.enum)
151
- })
152
-
153
- it('returns json schema with normalized required attributes', () => {
154
- const profileSchema = loadSync('examples/Profile.yaml')
155
-
156
- const { jsonSchema } = profileSchema
157
-
158
- expect(jsonSchema).to.have.property('type', 'object')
159
- expect(jsonSchema).to.have.property('properties')
160
- expect(jsonSchema.required).to.deep.equal([ 'name', 'contactDetails' ])
161
- expect(jsonSchema.properties.contactDetails.required).to.deep.equal([ 'email' ])
162
- })
163
-
164
- it('returns json schema without required attributes', () => {
165
- const schema = new Schema({ name: { type: 'string' } }, 'Entity')
166
-
167
- const { jsonSchema } = schema
168
-
169
- expect(jsonSchema.required).to.not.exist
170
- })
171
- })
172
- })
@@ -1,31 +0,0 @@
1
- 'use strict'
2
-
3
- const { pick } = require('lodash')
4
-
5
- class ValidationError extends Error {
6
- constructor(schemaId, invalidObject, validationErrors) {
7
- super(`"${schemaId}" validation failed`)
8
-
9
- this._object = invalidObject
10
- this._schemaId = schemaId
11
- this._validationErrors = validationErrors.map(error => pick(error, [
12
- 'path',
13
- 'code',
14
- 'params',
15
- 'message',
16
- 'schemaId'
17
- ]))
18
- }
19
-
20
- toJSON() {
21
- return {
22
- code: this.constructor.name,
23
- object: this._object,
24
- message: this.message,
25
- schemaId: this._schemaId,
26
- validationErrors: this._validationErrors
27
- }
28
- }
29
- }
30
-
31
- module.exports = ValidationError
package/src/Validator.js DELETED
@@ -1,128 +0,0 @@
1
- 'use strict'
2
-
3
- const { keyBy, groupBy } = require('lodash')
4
- const ZSchema = require('z-schema')
5
- const cleanupNulls = require('./helpers/cleanupNulls')
6
- const getReferenceIds = require('./helpers/getReferenceIds')
7
- const ValidationError = require('./ValidationError')
8
- const cleanupAttributes = require('./helpers/cleanupAttributes')
9
- const nullifyEmptyValues = require('./helpers/nullifyEmptyValues')
10
- const normalizeAttributes = require('./helpers/normalizeAttributes')
11
-
12
- class Validator {
13
- constructor(schemas = []) {
14
- if (schemas.length === 0) {
15
- throw new Error('No schemas provided')
16
- }
17
-
18
- const groupsById = groupBy(schemas, 'id')
19
-
20
- for (const id in groupsById) {
21
- const schemas = groupsById[id]
22
- const hasDuplicates = schemas.length > 1
23
-
24
- if (hasDuplicates) {
25
- throw new Error(`Multiple "${id}" schemas provided`)
26
- }
27
- }
28
-
29
- this._engine = new ZSchema({
30
- reportPathAsArray: false,
31
- ignoreUnknownFormats: true,
32
- })
33
-
34
- const jsonSchemas = schemas.map(({ jsonSchema }) => jsonSchema)
35
- const isValid = this._engine.validateSchema(jsonSchemas)
36
-
37
- if (!isValid) {
38
- const json = JSON.stringify(this._engine.lastReport.errors, null, 2)
39
- throw new Error(`Schemas validation failed:\n${json}`)
40
- }
41
-
42
- this._schemasMap = keyBy(schemas, 'id')
43
- this._jsonSchemasMap = keyBy(jsonSchemas, 'id')
44
- }
45
-
46
- validate(object, schemaId, shouldNullifyEmptyValues = false, shouldCleanupNulls = false) {
47
- const jsonSchema = this._jsonSchemasMap[schemaId]
48
-
49
- if (!jsonSchema) {
50
- throw new Error(`Schema "${schemaId}" not found`)
51
- }
52
-
53
- const objectJson = JSON.stringify(object)
54
- let result = JSON.parse(objectJson)
55
-
56
- if (shouldCleanupNulls) {
57
- result = cleanupNulls(result)
58
- }
59
-
60
- try {
61
- // NOTE: Drop attributes from objects that are not defined in schema.
62
- // This is bad for FE developers, as they continue to send some
63
- // trash to endpoints, but good for integrations with third party
64
- // services, e.g. Telegram, when you do not want to define schema
65
- // for the full payload. This method currently fails for cases when
66
- // attribute is defined as object or array in schema, but value is
67
- // a string. In this case validation method below would catch that.
68
- cleanupAttributes(result, jsonSchema, this._jsonSchemasMap)
69
-
70
- // NOTE: Normalize method helps to integrate objects built from URLs,
71
- // where types are not defined, e.g. booleans are '1', 'yes' string
72
- // or numbers are '1', '2'... strings.
73
- normalizeAttributes(result, jsonSchema, this._jsonSchemasMap)
74
-
75
- // eslint-disable-next-line no-unused-vars
76
- } catch (error) {
77
- // NOTE: Skip errors in cleanup and normalize attributes methods,
78
- // validation fails for objects with invalid value types.
79
-
80
- }
81
-
82
- const isValid = this._engine.validate(result, jsonSchema)
83
-
84
- if (isValid) {
85
- return result
86
- }
87
-
88
- let validationErrors = this._engine.getLastErrors()
89
-
90
- if (!shouldNullifyEmptyValues) {
91
- throw new ValidationError(schemaId, result, validationErrors)
92
- }
93
-
94
- const [ updatedResult, updatedValidationErrors ] = nullifyEmptyValues(result, validationErrors)
95
-
96
- const hasValidationErrors = updatedValidationErrors.length > 0
97
-
98
- if (hasValidationErrors) {
99
- throw new ValidationError(schemaId, result, updatedValidationErrors)
100
- }
101
-
102
- return updatedResult
103
- }
104
-
105
- normalize(object, schemaId) {
106
- const jsonSchema = this._jsonSchemasMap[schemaId]
107
-
108
- if (!jsonSchema) {
109
- throw new Error(`Schema "${schemaId}" not found`)
110
- }
111
-
112
- const result = JSON.parse(JSON.stringify(object))
113
- normalizeAttributes(result, jsonSchema, this._jsonSchemasMap)
114
-
115
- return result
116
- }
117
-
118
- get schemasMap() {
119
- return this._schemasMap
120
- }
121
-
122
- getReferenceIds(schemaId) {
123
- const schema = this._schemasMap[schemaId]
124
- return getReferenceIds(schema, this._schemasMap)
125
- }
126
- }
127
-
128
- module.exports = Validator