Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Schema: handle refinements where the 'from' part includes a tran… #3672

Merged
merged 1 commit into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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