Skip to content

Commit 7eb7257

Browse files
authored
New response type (#391)
1 parent 93e8792 commit 7eb7257

File tree

289 files changed

+11668
-12745
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

289 files changed

+11668
-12745
lines changed

README.md

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -84,62 +84,76 @@ endpoint of Elasticsearch and the respective type mapping. For example:
8484

8585
```jsonc
8686
{
87-
"types": [{
88-
"kind": "request",
89-
"name": {
90-
"namespace": "document.single.index",
91-
"name": "IndexRequest"
92-
},
93-
"description": "The document",
94-
"annotations": {
95-
"type_stability": "stable"
96-
},
97-
"generics": [
98-
"TDocument"
87+
"types": [ {
88+
"attachedBehaviors": [
89+
"CommonQueryParameters"
9990
],
100-
"inherits": [
101-
{
91+
"body": {
92+
"kind": "value",
93+
"value": {
94+
"kind": "instance_of",
10295
"type": {
103-
"namespace": "common_abstractions.request",
104-
"name": "RequestBase"
96+
"name": "TDocument",
97+
"namespace": "_global.index"
10598
}
10699
}
107-
],
108-
"path": [...],
109-
"query": [...],
110-
"body": {...}
111-
}, {
112-
"kind": "interface",
113-
"name": {
114-
"namespace": "document.single.index",
115-
"name": "IndexResponse"
116100
},
117-
"inherits": [
101+
"generics": [
118102
{
119-
"type": {
120-
"namespace": "document.single",
121-
"name": "WriteResponseBase"
122-
}
103+
"name": "TDocument",
104+
"namespace": "_global.index"
123105
}
124106
],
125-
"properties": []
126-
}],
127-
"endpoints": [{
128-
"name": "index",
129-
"description": "Creates or updates a document in an index.",
130-
"docUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-index_.html",
131-
"stability": "stable",
132-
"request": {
133-
"namespace": "document.single.index",
134-
"name": "IndexRequest"
107+
"inherits": {
108+
"type": {
109+
"name": "RequestBase",
110+
"namespace": "_types"
111+
}
112+
},
113+
"kind": "request",
114+
"name": {
115+
"name": "Request",
116+
"namespace": "_global.index"
135117
},
136-
"requestBodyRequired": true,
137-
"response": {
138-
"namespace": "document.single.index",
139-
"name": "IndexResponse"
118+
"path": [...],
119+
"query": [...]
120+
}, {
121+
"inherits": {
122+
"type": {
123+
"name": "WriteResponseBase",
124+
"namespace": "_types"
125+
}
140126
},
141-
"urls": [...]
142-
}]
127+
"kind": "response",
128+
"name": {
129+
"name": "Response",
130+
"namespace": "_global.index"
131+
}
132+
}],
133+
"endpoints": [{
134+
"accept": [
135+
"application/json"
136+
],
137+
"contentType": [
138+
"application/json"
139+
],
140+
"description": "Creates or updates a document in an index.",
141+
"docUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-index_.html",
142+
"name": "index",
143+
"request": {
144+
"name": "Request",
145+
"namespace": "_global.index"
146+
},
147+
"requestBodyRequired": true,
148+
"response": {
149+
"name": "Response",
150+
"namespace": "_global.index"
151+
},
152+
"since": "0.0.0",
153+
"stability": "stable",
154+
"urls": [...],
155+
"visibility": "public"
156+
}]
143157
}
144158
```
145159

compiler/model/build-model.ts

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import {
3939
getNameSpace,
4040
hoistRequestAnnotations,
4141
hoistTypeAnnotations,
42-
isApi,
4342
isKnownBehavior,
4443
modelBehaviors,
4544
modelEnumDeclaration,
@@ -160,61 +159,90 @@ export function compileSpecification (endpointMappings: Record<string, model.End
160159
return model
161160
}
162161

163-
function compileClassOrInterfaceDeclaration (declaration: ClassDeclaration | InterfaceDeclaration, mappings: Record<string, model.Endpoint>, allClasses: ClassDeclaration[]): model.Request | model.Interface {
162+
function compileClassOrInterfaceDeclaration (declaration: ClassDeclaration | InterfaceDeclaration, mappings: Record<string, model.Endpoint>, allClasses: ClassDeclaration[]): model.Request | model.Response | model.Interface {
164163
const name = declaration.getName()
165164
assert(declaration, name != null, 'Anonymous definitions should not exists')
166165

167-
if (name.endsWith('Request')) {
166+
if (name === 'Request') {
168167
assert(
169168
declaration,
170169
Node.isInterfaceDeclaration(declaration),
171170
`Request definitions must be declared as interfaces: ${name}`
172171
)
173172
}
174173

175-
if (name.endsWith('Response')) {
174+
if (name === 'Response') {
176175
assert(
177176
declaration,
178177
Node.isClassDeclaration(declaration),
179178
`Response definitions must be declared as classes: ${name}`
180179
)
181180
}
182181

183-
// Request definitions needs to be handled
182+
// Request and Response definitions needs to be handled
184183
// differently from normal classes
185-
if (Node.isInterfaceDeclaration(declaration) && isApi(declaration)) {
186-
const response: model.TypeName = {
187-
name: 'Response',
188-
namespace: getNameSpace(declaration)
189-
}
184+
if (name === 'Request' || name === 'Response') {
185+
let type: model.Request | model.Response
186+
if (name === 'Request') {
187+
type = {
188+
kind: 'request',
189+
name: { name, namespace: getNameSpace(declaration) },
190+
path: new Array<model.Property>(),
191+
query: new Array<model.Property>()
192+
}
190193

191-
const type: model.Request = {
192-
kind: 'request',
193-
name: { name, namespace: getNameSpace(declaration) },
194-
path: new Array<model.Property>(),
195-
query: new Array<model.Property>()
196-
}
194+
const response: model.TypeName = {
195+
name: 'Response',
196+
namespace: getNameSpace(declaration)
197+
}
197198

198-
hoistRequestAnnotations(type, declaration.getJsDocs(), mappings, response)
199+
hoistRequestAnnotations(type, declaration.getJsDocs(), mappings, response)
199200

200-
for (const member of declaration.getMembers()) {
201-
// we are visiting `path_parts, `query_parameters` or `body`
202-
assert(
203-
member,
204-
Node.isPropertyDeclaration(member) || Node.isPropertySignature(member),
205-
'Class and interfaces can only have property declarations or signatures'
206-
)
207-
const property = visitRequestProperty(member)
208-
if (property.name === 'path_parts') {
209-
type.path = property.properties
210-
} else if (property.name === 'query_parameters') {
211-
type.query = property.properties
212-
} else {
213-
// the body can either by a value (eg Array<string> or an object with properties)
214-
if (property.valueOf != null) {
215-
type.body = { kind: 'value', value: property.valueOf }
216-
} else if (property.properties.length > 0) {
217-
type.body = { kind: 'properties', properties: property.properties }
201+
for (const member of declaration.getMembers()) {
202+
// we are visiting `path_parts, `query_parameters` or `body`
203+
assert(
204+
member,
205+
Node.isPropertyDeclaration(member) || Node.isPropertySignature(member),
206+
'Class and interfaces can only have property declarations or signatures'
207+
)
208+
const property = visitRequestOrResponseProperty(member)
209+
if (property.name === 'path_parts') {
210+
type.path = property.properties
211+
} else if (property.name === 'query_parameters') {
212+
type.query = property.properties
213+
} else {
214+
// the body can either by a value (eg Array<string> or an object with properties)
215+
if (property.valueOf != null) {
216+
type.body = { kind: 'value', value: property.valueOf }
217+
} else if (property.properties.length > 0) {
218+
type.body = { kind: 'properties', properties: property.properties }
219+
}
220+
}
221+
}
222+
} else {
223+
type = {
224+
kind: 'response',
225+
name: { name, namespace: getNameSpace(declaration) },
226+
body: undefined
227+
}
228+
229+
for (const member of declaration.getMembers()) {
230+
// we are visiting `path_parts, `query_parameters` or `body`
231+
assert(
232+
member,
233+
Node.isPropertyDeclaration(member) || Node.isPropertySignature(member),
234+
'Class and interfaces can only have property declarations or signatures'
235+
)
236+
const property = visitRequestOrResponseProperty(member)
237+
if (property.name === 'body') {
238+
// the body can either by a value (eg Array<string> or an object with properties)
239+
if (property.valueOf != null) {
240+
type.body = { kind: 'value', value: property.valueOf }
241+
} else if (property.properties.length > 0) {
242+
type.body = { kind: 'properties', properties: property.properties }
243+
}
244+
} else {
245+
assert(member, false, 'Response.body is the only Response property supported')
218246
}
219247
}
220248
}
@@ -340,7 +368,7 @@ function compileClassOrInterfaceDeclaration (declaration: ClassDeclaration | Int
340368
* differently as are described as nested objects, and the body could have two
341369
* different types, `model.Property[]` (a normal object) or `model.ValueOf` (eg: an array or generic)
342370
*/
343-
function visitRequestProperty (member: PropertyDeclaration | PropertySignature): { name: string, properties: model.Property[], valueOf: model.ValueOf | null } {
371+
function visitRequestOrResponseProperty (member: PropertyDeclaration | PropertySignature): { name: string, properties: model.Property[], valueOf: model.ValueOf | null } {
344372
const properties: model.Property[] = []
345373
let valueOf: model.ValueOf | null = null
346374

compiler/model/metamodel.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class TypeName {
4949
/**
5050
* Type of a value. Used both for property types and nested type definitions.
5151
*/
52-
export type ValueOf = InstanceOf | ArrayOf | UnionOf | DictionaryOf | UserDefinedValue | LiteralValue
52+
export type ValueOf = InstanceOf | ArrayOf | UnionOf | DictionaryOf | UserDefinedValue | LiteralValue | VoidValue
5353

5454
/**
5555
* A single value
@@ -116,6 +116,13 @@ export class LiteralValue {
116116
value: string | number | boolean
117117
}
118118

119+
/**
120+
* The absence of any type. This is commonly used in APIs that returns an empty body.
121+
*/
122+
export class VoidValue {
123+
kind: 'void_value'
124+
}
125+
119126
/**
120127
* An interface or request interface property.
121128
*/
@@ -142,7 +149,7 @@ export class Property {
142149
// ------------------------------------------------------------------------------------------------
143150
// Type definitions
144151

145-
export type TypeDefinition = Interface | Request | Enum | TypeAlias
152+
export type TypeDefinition = Interface | Request | Response | Enum | TypeAlias
146153

147154
// ------------------------------------------------------------------------------------------------
148155

@@ -240,6 +247,19 @@ export class Request extends BaseType {
240247
attachedBehaviors?: string[]
241248
}
242249

250+
/**
251+
* A response type
252+
*/
253+
export class Response extends BaseType {
254+
kind: 'response'
255+
generics?: TypeName[]
256+
inherits?: Inherits
257+
implements?: Inherits[]
258+
body?: ValueBody | PropertiesBody
259+
behaviors?: Inherits[]
260+
attachedBehaviors?: string[]
261+
}
262+
243263
export class ValueBody {
244264
kind: 'value'
245265
value: ValueOf

compiler/model/utils.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ import { dirname } from 'path'
4545
*/
4646
export const knownBehaviors = [
4747
'AdditionalProperties',
48-
'ArrayResponseBase',
49-
'EmptyResponseBase',
5048
'CommonQueryParameters',
5149
'CommonCatQueryParameters'
5250
]
@@ -112,6 +110,17 @@ export function modelType (node: Node): model.ValueOf {
112110
return type
113111
}
114112

113+
case ts.SyntaxKind.VoidKeyword: {
114+
const type: model.InstanceOf = {
115+
kind: 'instance_of',
116+
type: {
117+
name: 'void',
118+
namespace: 'internal'
119+
}
120+
}
121+
return type
122+
}
123+
115124
// TODO: this should not be used in the specification
116125
// we should throw an error
117126
case ts.SyntaxKind.AnyKeyword: {
@@ -238,6 +247,13 @@ export function modelType (node: Node): model.ValueOf {
238247
return type
239248
}
240249

250+
case 'Void': {
251+
const type: model.VoidValue = {
252+
kind: 'void_value'
253+
}
254+
return type
255+
}
256+
241257
default: {
242258
const generics = node.getTypeArguments().map(node => modelType(node))
243259
const identifier = node.getTypeName()

0 commit comments

Comments
 (0)