Closed popematt closed 1 year ago
In the course of modeling PartiQL test cases in ion text, one proposal for a way to model the test cases was something like:
'literal'::[
'int'::parse:: "5",
'null'::parse:: "null",
// ...
'nested empty struct'::parse:: "{'a':{}}"
]
In attempting to model this in Ion Schema, it doesn't currently seem possible to:
parse
annotation, where that additional annotation would be the test nameThe annotations
constraint will be expanded to define the type and/or constraints for all annotations of a value. Annotations are symbols tokens but will be represented as a non-null list of non-null, un-annotated symbol values for the purpose of validation.
The existing annotations
syntax shall remain, since it is the most concise and readable syntax for use cases such as required::closed::[one_required_annotation]
.
<ANNOTATION> ::= <SYMBOL>
| required::<SYMBOL>
| optional::<SYMBOL>
<ANNOTATIONS_MODIFIER> ::= required::
| ordered::
| closed::
<ANNOTATIONS> ::= annotations: <ANNOTATIONS_MODIFIER>... [ <ANNOTATION>... ]
| annotations: <TYPE_REFERENCE>
This is an example of one way of implementing the constraint to serve as a reference for the correct behavior. There is no requirement that it must be implemented this way. This example covers only the new logic. Actual implementations should also include the existing annotations logic.
private fun test(value: IonValue, typeRef: Type): Boolean {
val annotationsList = value.typeAnnotations.mapTo(ionSystem.newEmptyList()) { ionSystem.newSymbol(it) }
return typeRef.isValid(annotationsList)
}
It would be possible to completely eliminate the existing annotations syntax since this is going into a new major version of Ion Schema and therefore is not required to be backwards compatible. However, the reasons for keeping the old syntax are twofold
Old Syntax | New Syntax |
---|---|
annotations: closed::required::['com.fooservice.Request'] |
annotations: { valid_values: [['com.fooservice.Request']] } or annotations: { contains: ['com.fooservice.Request'], container_length: 1 } or annotations: { ordered_elements: { valid_values: ['com.fooservice.Request'] } } |
annotations: required::['com.fooservice.Request'] |
annotations: { contains: ['com.fooservice.Request'] } |
annotations: closed::['com.fooservice.Request'] |
annotations: { valid_values: [[], ['com.fooservice.Request']] } or annotations: { ordered_elements: { occurs: optional, valid_values: ['com.fooservice.Request'] } } or annotations: { element: { valid_values: ['com.fooservice.Request'] }, container_length: range::[0, 1] } |
The behavior of some of the ISL 1.0 syntax is unexpected and/or not useful, so perhaps only part of the old syntax should be retained. By removing ordered::
, by requiring at least one of closed::
or required::
, and by eliminating the annotation-level optional::
modifier, we can eliminate all of the potentially confusing or useless cases mentioned in https://github.com/amzn/ion-schema-kotlin/issues/150, while also preserving the succinct syntax for the simplest use cases.
Resolved by #76 in Ion Schema 2.0
Problem
The
annotations
constraint has some confusing interactions between the modifiers (ordered::
,closed::
,required::
, andoptional::
); and without any modifiers, theannotations
constraint actually does nothing, which is a really confusing default behavior. Theannotations
constraint is also unable to model things like:Possible Solution
We could model annotations (approximately) as a list of symbol tokens, and allows the
annotations
constraint to take a<TYPE_REF>
as its argument.For example:
This can be done in a backwards compatible way that preserves the existing syntax because all
<TYPE_REF>
are either asymbol
or astruct
, which can be clearly distinguished from the existing syntax that is alist
of symbols.