Skip to content

Commit 430c619

Browse files
authored
Add support for OverloadOf behavior (#702)
1 parent e273cb2 commit 430c619

File tree

11 files changed

+350
-11
lines changed

11 files changed

+350
-11
lines changed

compiler/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ import addInfo from './steps/add-info'
2323
import addDescription from './steps/add-description'
2424
import validateModel from './steps/validate-model'
2525
import addContentType from './steps/add-content-type'
26+
import readDefinitionValidation from './steps/read-definition-validation'
2627

2728
const compiler = new Compiler()
2829

2930
compiler
3031
.generateModel()
3132
.step(addInfo)
3233
.step(addContentType)
34+
.step(readDefinitionValidation)
3335
.step(validateRestSpec)
3436
.step(addDescription)
3537
.step(validateModel)

compiler/model/utils.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ export const knownBehaviors = [
4747
'AdditionalProperties',
4848
'AdditionalProperty',
4949
'CommonQueryParameters',
50-
'CommonCatQueryParameters'
50+
'CommonCatQueryParameters',
51+
'OverloadOf'
5152
]
5253

5354
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import assert from 'assert'
21+
import * as model from '../model/metamodel'
22+
import { JsonSpec } from '../model/json-spec'
23+
import chalk from 'chalk'
24+
25+
/**
26+
* Verifies if "read" version of interface definitions
27+
* contains the same properties of their "write" version.
28+
* Then, it copies every model.Property from write to read but 'required'.
29+
*/
30+
export default async function readDefinitionValidation (model: model.Model, jsonSpec: Map<string, JsonSpec>): Promise<model.Model> {
31+
for (const type of model.types) {
32+
if (type.kind !== 'interface') continue
33+
const readBehavior = type.behaviors?.find(behavior => behavior.type.name === 'OverloadOf')
34+
if (readBehavior == null) continue
35+
assert(Array.isArray(readBehavior.generics))
36+
assert(readBehavior.generics[0].kind === 'instance_of')
37+
38+
for (const parent of model.types) {
39+
if (parent.name.name === readBehavior.generics[0].type.name && parent.name.namespace === readBehavior.generics[0].type.namespace) {
40+
assert(parent.kind === 'interface')
41+
const readProperties = type.properties.map(p => p.name)
42+
for (const property of parent.properties) {
43+
// have we defined the same properties?
44+
if (!readProperties.includes(property.name)) {
45+
console.log(chalk.red`The property '${property.name}' is present in ${parent.name.namespace}.${parent.name.name} but not in ${type.name.namespace}.${type.name.name}`)
46+
process.exit(1)
47+
}
48+
49+
const readProperty = type.properties.find(p => p.name === property.name) as model.Property
50+
// in the future we might want to have a different type between read and write defintions
51+
// but let's address it when it will happen
52+
if (!deepEqual(readProperty.type, property.type)) {
53+
console.log(chalk.red`The property '${property.name}' present in ${parent.name.namespace}.${parent.name.name} does not have the same type in ${type.name.namespace}.${type.name.name}`)
54+
process.exit(1)
55+
}
56+
57+
// we have the same properties, so let's copy the metadata
58+
for (const key in property) {
59+
if (key === 'required') continue
60+
readProperty[key] = property[key]
61+
}
62+
}
63+
}
64+
}
65+
}
66+
67+
return model
68+
}
69+
70+
function deepEqual (a: any, b: any): boolean {
71+
try {
72+
assert.deepStrictEqual(a, b)
73+
return true
74+
} catch (err) {
75+
return false
76+
}
77+
}

docs/behaviors.md

+17
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,20 @@ Since these can break the request structure these are listed explicitly as a beh
4444
```ts
4545
class CatRequestBase extends RequestBase implements CommonCatQueryParameters {}
4646
```
47+
48+
## OverloadOf
49+
50+
Defines a class that is the "overload" version of a definition used when writing a property.
51+
A class that implements `OverloadOf` should have the exact same properties with the same types.
52+
It can change if a property is required or not. There is no need to port the descriptions
53+
and js doc tags, the compiler will do that for you.
54+
55+
```ts
56+
export class Foo {
57+
bar?: string
58+
}
59+
60+
export class FooRead implements OverloadOf<Foo> {
61+
bar: string
62+
}
63+
```

output/schema/schema.json

+200-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)