Skip to content

Commit 2fd9b22

Browse files
committed
iop-openapi: fix schema details with ref to component
In OpenAPI, components schemas can be declared independently, and then referred to to avoid duplication. However, when referencing an existing schema, no other information can be provided. This means that a field of a struct whose type has its own schema must use a '$ref', but then cannot specify anything else, especially no default value, no description, no constraints... This is a pretty bad overlook from the OpenAPI spec: �ihttps://github.com/OAI/OpenAPI-Specification/issues/1514 The proposed workaround is to use an untyped schema with all the details, and push the $ref inside an allOf clause, on its own. This is what is done in this commit. As you can see, this is pretty bad semantically, and I can't really imagine expecting a client to properly understand what it means, but that's the solution... Change-Id: Ifdba5fe6a7a2494aa136ec3ea3f0952edd45d6d0 rip-it: 0c86011
1 parent c09cbf9 commit 2fd9b22

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

iop-openapi.blk

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,28 +578,35 @@ static scalar_value_t *t_attr_get_value(const iop_field_attr_t *attr,
578578
return NULL;
579579
}
580580

581-
static void
581+
static bool
582582
apply_raw_field_constraints(const iop_field_attrs_t *attrs,
583583
const iop_field_t *field,
584584
schema_object_t *schema)
585585
{
586+
bool did_update = false;
587+
586588
for (int i = 0; i < attrs->attrs_len; i++) {
587589
const iop_field_attr_t *attr = &attrs->attrs[i];
588590

589591
switch (attr->type) {
590592
case IOP_FIELD_MIN:
593+
did_update = true;
591594
schema->minimum = t_attr_get_value(attr, field->type);
592595
break;
593596
case IOP_FIELD_MAX:
597+
did_update = true;
594598
schema->maximum = t_attr_get_value(attr, field->type);
595599
break;
596600
case IOP_FIELD_MIN_LENGTH:
601+
did_update = true;
597602
OPT_SET(schema->min_length, attr->args[0].v.i64);
598603
break;
599604
case IOP_FIELD_MAX_LENGTH:
605+
did_update = true;
600606
OPT_SET(schema->max_length, attr->args[0].v.i64);
601607
break;
602608
case IOP_FIELD_PATTERN:
609+
did_update = true;
603610
schema->pattern = attr->args[0].v.s;
604611
break;
605612
default:
@@ -615,14 +622,18 @@ apply_raw_field_constraints(const iop_field_attrs_t *attrs,
615622
|| schema->minimum->type == SCALAR_UINT64)
616623
&& schema->minimum->i == 0)
617624
{
625+
did_update = true;
618626
schema->minimum->i = 1;
619627
}
620628
}
621629
if (TST_BIT(&attrs->flags, IOP_FIELD_NON_EMPTY)
622630
&& !OPT_ISSET(schema->min_length))
623631
{
632+
did_update = true;
624633
OPT_SET(schema->min_length, 1);
625634
}
635+
636+
return did_update;
626637
}
627638

628639
static void
@@ -678,6 +689,8 @@ t_get_iop_field_defval(const iop_field_t *desc)
678689
break;
679690

680691
case IOP_T_ENUM:
692+
/* FIXME: use string repr, as we type ENUM as string in the
693+
* schema */
681694
val->i = desc->u0.defval_enum;
682695
val->type = SCALAR_INT64;
683696
break;
@@ -701,6 +714,7 @@ t_iop_field_to_schema_object(const iop_struct_t *st, const iop_field_t *desc,
701714
schema_object_t *schema;
702715
const iop_help_t *help;
703716
bool is_v2;
717+
bool has_update = false;
704718

705719
if (attrs && TST_BIT(&attrs->flags, IOP_FIELD_PRIVATE)) {
706720
return NULL;
@@ -778,7 +792,9 @@ t_iop_field_to_schema_object(const iop_struct_t *st, const iop_field_t *desc,
778792
}
779793

780794
if (attrs) {
781-
apply_raw_field_constraints(attrs, desc, schema);
795+
has_update |= apply_raw_field_constraints(attrs, desc, schema);
796+
/* FIXME: must transform the schema into an allOf if it is a REF
797+
* and has_update is true */
782798
}
783799

784800
switch (desc->repeat) {
@@ -795,6 +811,7 @@ t_iop_field_to_schema_object(const iop_struct_t *st, const iop_field_t *desc,
795811
}
796812
} break;
797813
case IOP_R_DEFVAL:
814+
has_update = true;
798815
schema->defval = t_get_iop_field_defval(desc);
799816
break;
800817
default:
@@ -803,12 +820,27 @@ t_iop_field_to_schema_object(const iop_struct_t *st, const iop_field_t *desc,
803820

804821
get_field_help(attrs, &help, &is_v2);
805822
if (help) {
823+
has_update = true;
806824
schema->description = t_iop_help_to_string(help, false);
807825
if (is_v2) {
808826
schema->example = help->example;
809827
}
810828
}
811829

830+
if (has_update && schema->type == TYPE_REF) {
831+
/* We cannot specify any fields with the REF type... so
832+
* a allOf must be used to split the ref from the details */
833+
schema_object_t *ref;
834+
835+
t_qv_init(&schema->all_of, 1);
836+
ref = t_new(schema_object_t, 1);
837+
ref->type = TYPE_REF;
838+
ref->name = schema->name;
839+
qv_append(&schema->all_of, ref);
840+
schema->type = TYPE_NONE;
841+
schema->name = LSTR_NULL_V;
842+
}
843+
812844
return schema;
813845
}
814846

test-data/openapi/dox.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,9 @@ components:
297297
minimum: -2147483648
298298
maximum: 2147483647
299299
b:
300-
$ref: "#/components/schemas/tstiop_dox.MyStruct"
300+
description: "local comment for MyIface.funA.in.b\n\ncomment for b of funA.in"
301+
allOf:
302+
- $ref: "#/components/schemas/tstiop_dox.MyStruct"
301303
example: "{\"aParam\":1,\"b\":{\"fieldA\":11,\"fieldB\":12,\"fieldC\":13}}"
302304
tstiop_dox.MyClass:
303305
type: object

test-data/openapi/struct_g.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ components:
7070
type: string
7171
default: "fo\"o?cbar\u00e9\u00a9"
7272
k:
73-
$ref: "#/components/schemas/tstiop.MyEnumA"
73+
allOf:
74+
- $ref: "#/components/schemas/tstiop.MyEnumA"
75+
default: 2
7476
l:
7577
type: number
7678
format: double

0 commit comments

Comments
 (0)