Skip to content

Commit

Permalink
add more description annotations (#3673)
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti authored Sep 24, 2024
1 parent 090e41c commit ad7e1de
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 148 deletions.
1 change: 1 addition & 0 deletions .changeset/pink-files-dance.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
"@effect/platform": patch
"@effect/schema": patch
---

Expand Down
5 changes: 5 additions & 0 deletions .changeset/poor-pots-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

Add more description annotations.
9 changes: 3 additions & 6 deletions packages/platform-node/test/fixtures/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"in": "path",
"schema": {
"description": "a string that will be parsed into a number",
"title": "string",
"type": "string"
},
"required": true
Expand Down Expand Up @@ -283,7 +282,6 @@
"in": "path",
"schema": {
"description": "a string that will be parsed into a number",
"title": "string",
"type": "string"
},
"required": true
Expand All @@ -310,7 +308,7 @@
"type": "string"
},
"createdAt": {
"description": "a DateTime.Utc instance",
"description": "a string that will be parsed into a DateTime.Utc",
"type": "string"
}
},
Expand Down Expand Up @@ -427,7 +425,7 @@
"type": "string"
},
"createdAt": {
"description": "a DateTime.Utc instance",
"description": "a string that will be parsed into a DateTime.Utc",
"type": "string"
}
},
Expand Down Expand Up @@ -559,7 +557,6 @@
"in": "header",
"schema": {
"description": "a string that will be parsed into a number",
"title": "string",
"type": "string"
},
"required": false
Expand Down Expand Up @@ -588,7 +585,7 @@
"type": "string"
},
"createdAt": {
"description": "a DateTime.Utc instance",
"description": "a string that will be parsed into a DateTime.Utc",
"type": "string"
}
},
Expand Down
53 changes: 34 additions & 19 deletions packages/platform/src/OpenApiJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,33 @@ const getJsonSchemaAnnotations = (annotated: AST.Annotated): Annotations =>
default: AST.getDefaultAnnotation(annotated)
})

const removeDefaultJsonSchemaAnnotations = (
jsonSchemaAnnotations: Annotations,
ast: AST.AST
): Annotations => {
if (jsonSchemaAnnotations["title"] === ast.annotations[AST.TitleAnnotationId]) {
delete jsonSchemaAnnotations["title"]
}
if (jsonSchemaAnnotations["description"] === ast.annotations[AST.DescriptionAnnotationId]) {
delete jsonSchemaAnnotations["description"]
}
return jsonSchemaAnnotations
}

const getASTJsonSchemaAnnotations = (ast: AST.AST): Annotations => {
const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast)
switch (ast._tag) {
case "StringKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.stringKeyword)
case "NumberKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.numberKeyword)
case "BooleanKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.booleanKeyword)
default:
return jsonSchemaAnnotations
}
}

const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined => {
const type = ps.type
if (AST.isUnion(type) && Option.isNone(AST.getJSONSchemaAnnotation(type))) {
Expand Down Expand Up @@ -390,24 +417,12 @@ const go = (
...constAnyObject,
...getJsonSchemaAnnotations(ast)
}
case "StringKeyword": {
return ast === AST.stringKeyword ? { type: "string" } : {
type: "string",
...getJsonSchemaAnnotations(ast)
}
}
case "NumberKeyword": {
return ast === AST.numberKeyword ? { type: "number" } : {
type: "number",
...getJsonSchemaAnnotations(ast)
}
}
case "BooleanKeyword": {
return ast === AST.booleanKeyword ? { type: "boolean" } : {
type: "boolean",
...getJsonSchemaAnnotations(ast)
}
}
case "StringKeyword":
return { type: "string", ...getASTJsonSchemaAnnotations(ast) }
case "NumberKeyword":
return { type: "number", ...getASTJsonSchemaAnnotations(ast) }
case "BooleanKeyword":
return { type: "boolean", ...getASTJsonSchemaAnnotations(ast) }
case "BigIntKeyword":
throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
case "SymbolKeyword":
Expand Down Expand Up @@ -611,7 +626,7 @@ const go = (
}
}
return {
...getJsonSchemaAnnotations(ast.to),
...getASTJsonSchemaAnnotations(ast.to),
...go(ast.from, $defs, true, path),
...getJsonSchemaAnnotations(ast)
}
Expand Down
55 changes: 18 additions & 37 deletions packages/platform/test/OpenApiJsonSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,7 @@ schema (Declaration): DateFromSelf`
type: "string"
})
expectJSONSchema(Schema.String.annotations({}), {
type: "string",
description: "a string",
title: "string"
type: "string"
})
})

Expand All @@ -218,9 +216,7 @@ schema (Declaration): DateFromSelf`
type: "number"
}, false)
expectJSONSchema(Schema.Number.annotations({}), {
type: "number",
description: "a number",
title: "number"
type: "number"
}, false)
})

Expand All @@ -229,9 +225,7 @@ schema (Declaration): DateFromSelf`
type: "boolean"
})
expectJSONSchema(Schema.Boolean.annotations({}), {
type: "boolean",
description: "a boolean",
title: "boolean"
type: "boolean"
})
})

Expand Down Expand Up @@ -514,7 +508,6 @@ schema (Declaration): DateFromSelf`
"items": [
{
"type": "string",
"title": "string",
"description": "e"
},
{
Expand Down Expand Up @@ -1376,17 +1369,13 @@ schema (Suspend): <suspended schema>`
it("examples support", () => {
expectJSONSchema(Schema.String.annotations({ examples: ["a", "b"] }), {
"type": "string",
"title": "string",
"description": "a string",
"examples": ["a", "b"]
})
})

it("default support", () => {
expectJSONSchema(Schema.String.annotations({ default: "" }), {
"type": "string",
"title": "string",
"description": "a string",
"default": ""
})
})
Expand Down Expand Up @@ -1503,9 +1492,7 @@ schema (Suspend): <suspended schema>`
"$ref": "#/$defs/Name",
"$defs": {
"Name": {
"type": "string",
"description": "a string",
"title": "string"
"type": "string"
}
}
})
Expand Down Expand Up @@ -1866,9 +1853,8 @@ schema (Suspend): <suspended schema>`
],
"properties": {
"a": {
"type": "string",
description: "a string that will be parsed into a number",
title: "string"
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -2066,9 +2052,8 @@ schema (Suspend): <suspended schema>`
a: {
contentMediaType: "application/json",
contentSchema: {
type: "string",
description: "a string that will be parsed into a number",
title: "string"
type: "string"
},
type: "string"
}
Expand Down Expand Up @@ -2110,15 +2095,14 @@ schema (Suspend): <suspended schema>`
value: Schema.NumberFromString
}),
{
description: "ReadonlyMap<a string at least 2 character(s) long, number>",
type: "object",
required: [],
properties: {},
"description": "a record that will be parsed into a ReadonlyMap",
"type": "object",
"required": [],
"properties": {},
"patternProperties": {
"": {
description: "a string that will be parsed into a number",
title: "string",
type: "string"
"description": "a string that will be parsed into a number",
"type": "string"
}
},
"propertyNames": {
Expand All @@ -2137,15 +2121,14 @@ schema (Suspend): <suspended schema>`
value: Schema.NumberFromString
}),
{
type: "object",
description: "Map<a string at least 2 character(s) long, number>",
required: [],
properties: {},
"type": "object",
"description": "a record that will be parsed into a Map",
"required": [],
"properties": {},
"patternProperties": {
"": {
description: "a string that will be parsed into a number",
title: "string",
type: "string"
"description": "a string that will be parsed into a number",
"type": "string"
}
},
"propertyNames": {
Expand Down Expand Up @@ -2173,7 +2156,6 @@ schema (Suspend): <suspended schema>`
Schema.DateFromString,
{
"type": "string",
"title": "string",
"description": "a string that will be parsed into a Date"
}
)
Expand All @@ -2184,7 +2166,6 @@ schema (Suspend): <suspended schema>`
Schema.Date,
{
"type": "string",
"title": "string",
"description": "a string that will be parsed into a Date"
}
)
Expand Down
58 changes: 39 additions & 19 deletions packages/schema/src/JSONSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,33 @@ const getJsonSchemaAnnotations = (annotated: AST.Annotated): JsonSchemaAnnotatio
default: AST.getDefaultAnnotation(annotated)
})

const removeDefaultJsonSchemaAnnotations = (
jsonSchemaAnnotations: JsonSchemaAnnotations,
ast: AST.AST
): JsonSchemaAnnotations => {
if (jsonSchemaAnnotations["title"] === ast.annotations[AST.TitleAnnotationId]) {
delete jsonSchemaAnnotations["title"]
}
if (jsonSchemaAnnotations["description"] === ast.annotations[AST.DescriptionAnnotationId]) {
delete jsonSchemaAnnotations["description"]
}
return jsonSchemaAnnotations
}

const getASTJsonSchemaAnnotations = (ast: AST.AST): JsonSchemaAnnotations => {
const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast)
switch (ast._tag) {
case "StringKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.stringKeyword)
case "NumberKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.numberKeyword)
case "BooleanKeyword":
return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.booleanKeyword)
default:
return jsonSchemaAnnotations
}
}

const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined => {
const type = ps.type
if (AST.isUnion(type) && Option.isNone(AST.getJSONSchemaAnnotation(type))) {
Expand Down Expand Up @@ -306,20 +333,6 @@ const isOverrideAnnotation = (jsonSchema: JsonSchema7): boolean => {
("enum" in jsonSchema) || ("$ref" in jsonSchema)
}

const removeDefaultJsonSchemaAnnotations = (out: JsonSchema7, def: AST.AST, ast: AST.AST): JsonSchema7 => {
if (def === ast) {
return out
}
const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast)
if (jsonSchemaAnnotations["title"] === def.annotations[AST.TitleAnnotationId]) {
delete jsonSchemaAnnotations["title"]
}
if (jsonSchemaAnnotations["description"] === def.annotations[AST.DescriptionAnnotationId]) {
delete jsonSchemaAnnotations["description"]
}
return merge(out, jsonSchemaAnnotations)
}

const go = (
ast: AST.AST,
$defs: Record<string, JsonSchema7>,
Expand All @@ -333,9 +346,16 @@ const go = (
const t = getRefinementInnerTransformation(ast)
if (t === undefined) {
try {
return merge(merge(go(ast.from, $defs, true, path), getJsonSchemaAnnotations(ast)), handler)
return {
...go(ast.from, $defs, true, path),
...getJsonSchemaAnnotations(ast),
...handler
}
} catch (e) {
return merge(getJsonSchemaAnnotations(ast), handler)
return {
...getJsonSchemaAnnotations(ast),
...handler
}
}
} else if (!isOverrideAnnotation(handler)) {
return go(t, $defs, true, path)
Expand Down Expand Up @@ -386,11 +406,11 @@ const go = (
case "ObjectKeyword":
return merge(objectJsonSchema, getJsonSchemaAnnotations(ast))
case "StringKeyword":
return removeDefaultJsonSchemaAnnotations({ type: "string" }, AST.stringKeyword, ast)
return { type: "string", ...getASTJsonSchemaAnnotations(ast) }
case "NumberKeyword":
return removeDefaultJsonSchemaAnnotations({ type: "number" }, AST.numberKeyword, ast)
return { type: "number", ...getASTJsonSchemaAnnotations(ast) }
case "BooleanKeyword":
return removeDefaultJsonSchemaAnnotations({ type: "boolean" }, AST.booleanKeyword, ast)
return { type: "boolean", ...getASTJsonSchemaAnnotations(ast) }
case "BigIntKeyword":
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast))
case "SymbolKeyword":
Expand Down
Loading

0 comments on commit ad7e1de

Please sign in to comment.