Skip to content

Commit 94a2249

Browse files
committed
Implement OrderedDictionary<K, V> type
1 parent f68285f commit 94a2249

File tree

9 files changed

+525
-13
lines changed

9 files changed

+525
-13
lines changed

compiler/src/model/metamodel.ts

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export class DictionaryOf {
8888
key: ValueOf
8989
value: ValueOf
9090
singleKey: boolean
91+
ordered: boolean
9192
}
9293

9394
/**

compiler/src/model/utils.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,8 @@ export function modelType (node: Node): model.ValueOf {
281281
kind: 'dictionary_of',
282282
key,
283283
value,
284-
singleKey: false
284+
singleKey: false,
285+
ordered: false
285286
}
286287
return type
287288
}
@@ -294,7 +295,21 @@ export function modelType (node: Node): model.ValueOf {
294295
kind: 'dictionary_of',
295296
key,
296297
value,
297-
singleKey: true
298+
singleKey: true,
299+
ordered: false
300+
}
301+
return type
302+
}
303+
304+
case 'OrderedDictionary': {
305+
assert(node, node.getTypeArguments().length === 2, 'A OrderedDictionary must have two arguments')
306+
const [key, value] = node.getTypeArguments().map(node => modelType(node))
307+
const type: model.DictionaryOf = {
308+
kind: 'dictionary_of',
309+
key,
310+
value,
311+
singleKey: false,
312+
ordered: true
298313
}
299314
return type
300315
}
@@ -500,7 +515,7 @@ export function modelEnumDeclaration (declaration: EnumDeclaration): model.Enum
500515
}
501516

502517
if (typeof tags.es_quirk === 'string') {
503-
type.esQuirk = tags.es_quirk
518+
type.esQuirk = tags.es_quirk.replace(/\r/g, '')
504519
}
505520

506521
return type
@@ -892,7 +907,7 @@ function hoistPropertyAnnotations (property: model.Property, jsDocs: JSDoc[]): v
892907
assert(jsDocs, value === 'container_property', `Unknown 'variant' value '${value}' on property ${property.name}`)
893908
property.containerProperty = true
894909
} else if (tag === 'es_quirk') {
895-
property.esQuirk = value
910+
property.esQuirk = value.replace(/\r/g, '')
896911
} else {
897912
assert(jsDocs, false, `Unhandled tag: '${tag}' with value: '${value}' on property ${property.name}`)
898913
}

compiler/src/steps/validate-model.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -396,17 +396,17 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
396396
const inheritedProps = inheritedProperties(typeDef)
397397

398398
context.push('path')
399-
validateProperties(typeDef.path, openGenerics, inheritedProps)
399+
validateProperties(fqn(typeDef.name), typeDef.path, openGenerics, inheritedProps)
400400
context.pop()
401401

402402
context.push('query')
403-
validateProperties(typeDef.query, openGenerics, inheritedProps)
403+
validateProperties(fqn(typeDef.name), typeDef.query, openGenerics, inheritedProps)
404404
context.pop()
405405

406406
context.push('body')
407407
switch (typeDef.body.kind) {
408408
case 'properties':
409-
validateProperties(typeDef.body.properties, openGenerics, inheritedProps)
409+
validateProperties(fqn(typeDef.name), typeDef.body.properties, openGenerics, inheritedProps)
410410
break
411411
case 'value':
412412
validateValueOf(typeDef.body.value, openGenerics)
@@ -433,7 +433,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
433433

434434
switch (typeDef.body.kind) {
435435
case 'properties':
436-
validateProperties(typeDef.body.properties, openGenerics, inheritedProperties(typeDef))
436+
validateProperties(fqn(typeDef.name), typeDef.body.properties, openGenerics, inheritedProperties(typeDef))
437437
break
438438
case 'value':
439439
validateValueOf(typeDef.body.value, openGenerics)
@@ -507,7 +507,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
507507

508508
validateInherits(typeDef.inherits, openGenerics)
509509
validateBehaviors(typeDef, openGenerics)
510-
validateProperties(typeDef.properties, openGenerics, inheritedProperties(typeDef))
510+
validateProperties(fqn(typeDef.name), typeDef.properties, openGenerics, inheritedProperties(typeDef))
511511

512512
if (typeDef.variants?.kind === 'container') {
513513
const variants = typeDef.properties.filter(prop => !(prop.containerProperty ?? false))
@@ -747,7 +747,7 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
747747
return false
748748
}
749749

750-
function validateProperties (props: model.Property[], openGenerics: Set<string>, inheritedProperties: Set<string>): void {
750+
function validateProperties (type: string, props: model.Property[], openGenerics: Set<string>, inheritedProperties: Set<string>): void {
751751
const allIdentifiers = new Set<string>()
752752
const allNames = new Set<string>()
753753

@@ -773,6 +773,13 @@ export default async function validateModel (apiModel: model.Model, restSpec: Ma
773773
}
774774

775775
context.push(`Property '${prop.name}'`)
776+
777+
if (prop.type.kind === 'dictionary_of' && prop.type.ordered) {
778+
if (prop.name !== 'aggregations') {
779+
modelError(`OrderedDictionary can not be used for property '${prop.name}' on type '${type}'.`)
780+
}
781+
}
782+
776783
validateValueOf(prop.type, openGenerics)
777784
validateValueOfJsonEvents(prop.type)
778785
context.pop()

compiler/src/transform/expand-generics.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@ export function expandGenerics (inputModel: Model, config?: ExpansionConfig): Mo
359359
kind: 'dictionary_of',
360360
key: expandValueOf(value.key, mappings),
361361
value: expandValueOf(value.value, mappings),
362-
singleKey: value.singleKey
362+
singleKey: value.singleKey,
363+
ordered: value.ordered
363364
}
364365

365366
case 'instance_of': {

docs/modeling-guide.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,24 @@ For example:
2424
```json
2525
{
2626
"property1": "type",
27-
"property2": "other-type",
27+
"property2": "other-type"
2828
}
2929
```
3030

31+
### OrderedDictionary
32+
33+
Represents a dynamic key value map that preserves the order of items:
34+
35+
```ts
36+
property: OrderedDictionary<string, TypeDefinition>
37+
```
38+
39+
The JSON specification and most dictionary implementations do not make guarantees about item ordering.
40+
`OrderedDictionary` can be used to express this fact in the Elasticsearch specification.
41+
42+
> [!WARNING]
43+
> This type should only be used for legacy types and should otherwise be avoided as far as possible.
44+
3145
### SingleKeyDictionary
3246

3347
Represents a dynamic key value map with a single top level key:

0 commit comments

Comments
 (0)