Skip to content

Commit

Permalink
JSON Schema: handle refinements where the 'from' part includes a tran… (
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti authored Sep 24, 2024
1 parent 2fcb542 commit 090e41c
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 5 deletions.
39 changes: 39 additions & 0 deletions .changeset/silly-grapes-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
"@effect/platform": patch
"@effect/schema": patch
---

JSON Schema: handle refinements where the 'from' part includes a transformation, closes #3662

Before

```ts
import { JSONSchema, Schema } from "@effect/schema"

const schema = Schema.Date

console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
/*
throws
Error: Missing annotation
details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation
schema (Refinement): Date
*/
```

After

```ts
import { JSONSchema, Schema } from "@effect/schema"

const schema = Schema.Date

console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
/*
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"description": "a string that will be parsed into a Date"
}
*/
```
38 changes: 38 additions & 0 deletions .changeset/thirty-moose-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
"@effect/schema": patch
---

Add description annotation to the encoded part of DateFromString.

Before

```ts
import { JSONSchema, Schema } from "@effect/schema"

const schema = Schema.DateFromString

console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
/*
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string"
}
*/
```

After

```ts
import { JSONSchema, Schema } from "@effect/schema"

const schema = Schema.DateFromString

console.log(JSON.stringify(JSONSchema.make(schema), null, 2))
/*
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"description": "a string that will be parsed into a Date"
}
*/
```
7 changes: 5 additions & 2 deletions packages/platform/src/OpenApiJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ const go = (
...getJsonSchemaAnnotations(ast)
}
}
if (handleIdentifier && !AST.isTransformation(ast)) {
if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) {
const identifier = getJSONIdentifier(ast)
if (Option.isSome(identifier)) {
const id = identifier.value
Expand Down Expand Up @@ -573,7 +573,10 @@ const go = (
}
}
case "Refinement": {
throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
if (AST.encodedBoundAST(ast) === ast) {
throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
}
return go(ast.from, $defs, true, path)
}
case "TemplateLiteral": {
const regex = AST.getTemplateLiteralRegExp(ast)
Expand Down
22 changes: 22 additions & 0 deletions packages/platform/test/OpenApiJsonSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2167,4 +2167,26 @@ schema (Suspend): <suspended schema>`
}
)
})

it("DateFromString", () => {
expectJSONSchema(
Schema.DateFromString,
{
"type": "string",
"title": "string",
"description": "a string that will be parsed into a Date"
}
)
})

it("Date", () => {
expectJSONSchema(
Schema.Date,
{
"type": "string",
"title": "string",
"description": "a string that will be parsed into a Date"
}
)
})
})
7 changes: 5 additions & 2 deletions packages/schema/src/JSONSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ const go = (
if (Option.isSome(surrogate)) {
return go(surrogate.value, $defs, handleIdentifier, path)
}
if (handleIdentifier && !AST.isTransformation(ast)) {
if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) {
const identifier = AST.getJSONIdentifier(ast)
if (Option.isSome(identifier)) {
const id = identifier.value
Expand Down Expand Up @@ -550,7 +550,10 @@ const go = (
}, getJsonSchemaAnnotations(ast))
}
case "Refinement": {
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast))
if (AST.encodedBoundAST(ast) === ast) {
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast))
}
return go(ast.from, $defs, true, path)
}
case "TemplateLiteral": {
const regex = AST.getTemplateLiteralRegExp(ast)
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6127,7 +6127,7 @@ export class ValidDateFromSelf extends DateFromSelf.pipe(
* @since 0.67.0
*/
export class DateFromString extends transform(
String$,
String$.annotations({ description: "a string that will be parsed into a Date" }),
DateFromSelf,
{ strict: true, decode: (s) => new Date(s), encode: (d) => util_.formatDate(d) }
).annotations({ identifier: "DateFromString" }) {}
Expand Down
22 changes: 22 additions & 0 deletions packages/schema/test/JSONSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2260,6 +2260,28 @@ schema (Suspend): <suspended schema>`
}
)
})

it("DateFromString", () => {
expectJSONSchema(
Schema.DateFromString,
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"description": "a string that will be parsed into a Date"
}
)
})

it("Date", () => {
expectJSONSchema(
Schema.Date,
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"description": "a string that will be parsed into a Date"
}
)
})
})

export const decode = <A>(schema: JSONSchema.JsonSchema7Root): Schema.Schema<A> =>
Expand Down

0 comments on commit 090e41c

Please sign in to comment.