Skip to content

Commit

Permalink
feat: OpenApi Parser string formats and nullable types (#1981)
Browse files Browse the repository at this point in the history
  • Loading branch information
RohinBhargava authored Jan 10, 2025
1 parent fd9b600 commit c9268c6
Show file tree
Hide file tree
Showing 55 changed files with 1,301 additions and 793 deletions.
5 changes: 5 additions & 0 deletions fern/apis/fdr/definition/api/latest/type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ types:
id: TypeReferenceId
primitive: PrimitiveType
optional: OptionalType
nullable: NullableType
list: ListType
set: SetType
map: MapType
Expand Down Expand Up @@ -136,6 +137,10 @@ types:
isOptional: boolean
contentType: optional<ContentType>

NullableType:
properties:
shape: TypeShape

OptionalType:
properties:
shape: TypeShape
Expand Down
1 change: 1 addition & 0 deletions fern/apis/fdr/definition/api/v1/read/type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ types:

StringType:
properties:
format: optional<string>
regex: optional<string>
minLength: optional<integer>
maxLength: optional<integer>
Expand Down
1 change: 1 addition & 0 deletions fern/apis/fdr/definition/api/v1/register/type.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ types:

StringType:
properties:
format: optional<string>
regex: optional<string>
minLength: optional<integer>
maxLength: optional<integer>
Expand Down
1 change: 1 addition & 0 deletions packages/fdr-sdk/src/api-definition/__test__/join.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const PRIMITIVE_SHAPE: Latest.TypeReference.Primitive = {
type: "primitive" as const,
value: {
type: "string",
format: undefined,
regex: undefined,
minLength: undefined,
maxLength: undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/fdr-sdk/src/api-definition/__test__/prune.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const PRIMITIVE_SHAPE: Latest.TypeReference.Primitive = {
type: "primitive" as const,
value: {
type: "string",
format: undefined,
regex: undefined,
minLength: undefined,
maxLength: undefined,
Expand Down
150 changes: 79 additions & 71 deletions packages/fdr-sdk/src/api-definition/__test__/unwrap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const PRIMITIVE_SHAPE: TypeShape = {
type: "primitive" as const,
value: {
type: "string",
format: undefined,
regex: undefined,
minLength: undefined,
maxLength: undefined,
Expand All @@ -24,26 +25,28 @@ const PRIMITIVE_SHAPE: TypeShape = {

describe("unwrapReference", () => {
it("should noop for a non-reference", () => {
expect(unwrapReference(PRIMITIVE_SHAPE["value"], {})).toMatchInlineSnapshot(
expect(unwrapReference(PRIMITIVE_SHAPE.value, {})).toMatchInlineSnapshot(
`
{
"availability": undefined,
{
"availability": undefined,
"default": undefined,
"descriptions": [],
"isNullable": false,
"isOptional": false,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"descriptions": [],
"isOptional": false,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {},
}
`
"format": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {},
}
`
);
});

Expand All @@ -63,26 +66,28 @@ describe("unwrapReference", () => {
};
expect(unwrapReference(shape, types)).toMatchInlineSnapshot(
`
{
"availability": undefined,
{
"availability": undefined,
"default": undefined,
"descriptions": [],
"isNullable": false,
"isOptional": false,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"descriptions": [],
"isOptional": false,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {
"foo",
},
}
`
"format": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {
"foo",
},
}
`
);
});

Expand All @@ -93,20 +98,21 @@ describe("unwrapReference", () => {
default: undefined,
};
expect(unwrapReference(shape, {})).toMatchInlineSnapshot(`
{
"availability": undefined,
"default": undefined,
"descriptions": [],
"isOptional": false,
"shape": {
"displayName": undefined,
"type": "unknown",
},
"visitedTypeIds": Set {
"foo",
},
}
`);
{
"availability": undefined,
"default": undefined,
"descriptions": [],
"isNullable": false,
"isOptional": false,
"shape": {
"displayName": undefined,
"type": "unknown",
},
"visitedTypeIds": Set {
"foo",
},
}
`);
});

it("should unwrap optionals", () => {
Expand All @@ -132,26 +138,28 @@ describe("unwrapReference", () => {
};
expect(unwrapReference(shape, types)).toMatchInlineSnapshot(
`
{
"availability": undefined,
{
"availability": undefined,
"default": undefined,
"descriptions": [],
"isNullable": false,
"isOptional": true,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"descriptions": [],
"isOptional": true,
"shape": {
"type": "primitive",
"value": {
"default": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {
"foo",
},
}
`
"format": undefined,
"maxLength": undefined,
"minLength": undefined,
"regex": undefined,
"type": "string",
},
},
"visitedTypeIds": Set {
"foo",
},
}
`
);
});

Expand Down Expand Up @@ -271,7 +279,7 @@ describe("unwrapReference", () => {
},
};
const unwrapped = unwrapReference(shape, types);
expect(unwrapped.shape).toStrictEqual(PRIMITIVE_SHAPE["value"]);
expect(unwrapped.shape).toStrictEqual(PRIMITIVE_SHAPE.value);
expect(unwrapped.availability).toBe("Deprecated");
expect(unwrapped.descriptions).toStrictEqual(["a", "b", "c"]);
expect(unwrapped.isOptional).toBe(true);
Expand Down
1 change: 1 addition & 0 deletions packages/fdr-sdk/src/api-definition/sort-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function sortKeysByShape(
primitive: () => obj,
literal: () => obj,
optional: ({ shape }) => sortKeysByShape(obj, shape, types),
nullable: ({ shape }) => sortKeysByShape(obj, shape, types),
list: ({ itemShape }) =>
Array.isArray(obj)
? obj.map((o) => sortKeysByShape(o, itemShape, types))
Expand Down
1 change: 1 addition & 0 deletions packages/fdr-sdk/src/api-definition/typeid-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class ApiTypeIdVisitor {
id: (value) => visit(value.id),
primitive: noop,
optional: (value) => ApiTypeIdVisitor.visitTypeShape(value.shape, visit),
nullable: (value) => ApiTypeIdVisitor.visitTypeShape(value.shape, visit),
list: (value) => ApiTypeIdVisitor.visitTypeShape(value.itemShape, visit),
set: (value) => ApiTypeIdVisitor.visitTypeShape(value.itemShape, visit),
map: (value) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/fdr-sdk/src/api-definition/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ export type DereferencedTypeShapeOrReference =
| DereferencedTypeReference;
export type DereferencedNonOptionalTypeShapeOrReference = Exclude<
DereferencedTypeShapeOrReference,
Latest.TypeReference.Optional
Latest.TypeReference.Optional | Latest.TypeReference.Nullable
>;
12 changes: 7 additions & 5 deletions packages/fdr-sdk/src/api-definition/unwrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type UnwrappedReference = {
descriptions: FernDocs.MarkdownText[];
visitedTypeIds: Set<Latest.TypeId>;
isOptional: boolean;
isNullable: boolean;
default?: unknown;
};

Expand Down Expand Up @@ -74,6 +75,7 @@ export function unwrapReference(
}

let isOptional = false;
let isNullable = false;
const defaults: InternalDefaultValue[] = [];
const descriptions: FernDocs.MarkdownText[] = [];
const availabilities: Latest.Availability[] = [];
Expand All @@ -89,7 +91,10 @@ export function unwrapReference(
break;
}

if (internalTypeRef.type === "optional") {
if (internalTypeRef.type === "nullable") {
isNullable = true;
internalTypeRef = internalTypeRef.shape;
} else if (internalTypeRef.type === "optional") {
isOptional = true;
if (internalTypeRef.default != null) {
defaults.push({
Expand Down Expand Up @@ -137,12 +142,10 @@ export function unwrapReference(
if (internalTypeRef == null) {
// Note: this should be a fatal error, but we're handling it gracefully for now
if (circularReference) {
// eslint-disable-next-line no-console
console.error(
`Circular reference detected. Falling back to unknown type. types=[${[...visitedTypeIds].join(", ")}]`
);
} else {
// eslint-disable-next-line no-console
console.error(
`Type reference is invalid. Falling back to unknown type. types=[${[...visitedTypeIds].join(", ")}]`
);
Expand All @@ -154,6 +157,7 @@ export function unwrapReference(
availability: coalesceAvailability(availabilities),
visitedTypeIds,
isOptional,
isNullable,
default: selectDefaultValue(internalTypeRef, defaults),
descriptions,
};
Expand Down Expand Up @@ -235,7 +239,6 @@ export function unwrapObjectType(
const extendedProperties = object.extends.flatMap(
(typeId): Latest.ObjectProperty[] => {
if (parentVisitedTypeIds?.has(typeId)) {
// eslint-disable-next-line no-console
console.error(
`Circular reference detected. Cannot extend type=${typeId}`
);
Expand All @@ -256,7 +259,6 @@ export function unwrapObjectType(

// TODO: should we be able to extend discriminated and undiscriminated unions?
if (unwrapped?.shape.type !== "object") {
// eslint-disable-next-line no-console
console.error("Object extends non-object", typeId);
return [];
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ function stringifyTypeRef(
primitive: (value) => value.value.type,
optional: (value) =>
`${stringifyTypeShape(value.shape, types, depth + 1)}?`,
nullable: (value) =>
`${stringifyTypeShape(value.shape, types, depth + 1)} | null`,
list: (value) =>
`List<${stringifyTypeShape(value.itemShape, types, depth + 1)}>`,
set: (value) =>
Expand Down
Loading

0 comments on commit c9268c6

Please sign in to comment.