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

Respect required fields in {target,} schema #7

Merged
merged 2 commits into from
May 1, 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
73 changes: 73 additions & 0 deletions _example/doc/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,76 @@
"detail"
]
},
"misc": {
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"title": "Misc",
"description": "Misc",
"stability": "prototype",
"strictProperties": true,
"type": [
"object"
],
"definitions": {
"id": {
"description": "misc id",
"example": "ec0a1edc-062e-11e7-8b1e-040ccee2aa06",
"readOnly": true,
"format": "uuid",
"type": [
"string"
]
}
},
"links": [
{
"description": "Register bool value",
"href": "/bool/register",
"title": "detail",
"method": "POST",
"rel": "create",
"schema": {
"properties": {
"bool": {
"description": "bool",
"example": true,
"type": [
"boolean"
]
}
},
"required": [
"bool"
]
},
"targetSchema": {
"properties": {
"id": {
"$ref": "#/definitions/misc/definitions/id"
},
"isTrue": {
"description": "isTrue",
"example": true,
"type": [
"boolean"
]
}
},
"required": [
"id",
"isTrue"
]
}
}
],
"properties": {
"id": {
"$ref": "#/definitions/user/definitions/id"
}
},
"required": [
"id"
]
},
"task": {
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"title": "Task",
Expand Down Expand Up @@ -353,6 +423,9 @@
"error": {
"$ref": "#/definitions/error"
},
"misc": {
"$ref": "#/definitions/misc"
},
"task": {
"$ref": "#/definitions/task"
},
Expand Down
55 changes: 55 additions & 0 deletions _example/doc/schema/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Tasky-App-Version 1.0.0
## The table of contents

- <a href="#resource-error">Error</a>
- <a href="#resource-misc">Misc</a>
- <a href="#link-POST-misc-/bool/register">POST /bool/register</a>
- <a href="#resource-task">Task</a>
- <a href="#link-GET-task-/tasks/{(%23%2Fdefinitions%2Ftask%2Fdefinitions%2Fidentity)}">GET /tasks/{task_id}</a>
- <a href="#link-POST-task-/tasks">POST /tasks</a>
Expand All @@ -47,6 +49,59 @@ This resource represents API error
| **errorFields/name** | *string* | param field name | `"status"` |


## <a name="resource-misc">Misc</a>

Stability: `prototype`

Misc

### Attributes

| Name | Type | Description | Example |
| ------- | ------- | ------- | ------- |
| **[id](#resource-user)** | *uuid* | user id | `"ec0a1edc-062e-11e7-8b1e-040ccee2aa06"` |

### <a name="link-POST-misc-/bool/register">Misc detail</a>

Register bool value

```
POST /bool/register
```

#### Required Parameters

| Name | Type | Description | Example |
| ------- | ------- | ------- | ------- |
| **bool** | *boolean* | bool | `true` |



#### Curl Example

```bash
$ curl -n -X POST https://tasky.io/v1/bool/register \
-d '{
"bool": true
}' \
-H "Content-Type: application/json"
```


#### Response Example

```
HTTP/1.1 201 Created
```

```json
{
"id": "ec0a1edc-062e-11e7-8b1e-040ccee2aa06",
"isTrue": true
}
```


## <a name="resource-task">Task</a>

Stability: `prototype`
Expand Down
48 changes: 48 additions & 0 deletions _example/doc/schema/schemata/misc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
"$schema": http://json-schema.org/draft-04/hyper-schema
title: Misc
description: Misc
stability: prototype
strictProperties: true
type:
- object

definitions:
id:
description: misc id
example: "ec0a1edc-062e-11e7-8b1e-040ccee2aa06"
readOnly: true
format: uuid
type: string

links:
- description: "Register bool value"
href: "/bool/register"
title: detail
method: POST
rel: create
schema:
properties:
bool:
description: bool
example: true
type: boolean
required:
- bool
targetSchema:
properties:
id:
$ref: "/schemata/misc#/definitions/id"
isTrue:
description: isTrue
example: true
type: boolean
required:
- id
- isTrue
properties:
id:
$ref: "/schemata/user#/definitions/id"
required:
- id
id: schemata/misc
15 changes: 15 additions & 0 deletions _example/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ type Error struct {
} `json:"errorFields,omitempty"`
}

// Misc struct for misc resource
type Misc struct {
ID string `json:"id"`
}

// Task struct for task resource
type Task struct {
CompletedAt time.Time `json:"completedAt"`
Expand All @@ -31,6 +36,16 @@ type User struct {
Name string `json:"name"`
}

// MiscCreateRequest struct for misc
// POST: /bool/register
type MiscCreateRequest struct {
Bool bool `json:"bool,omitempty"`
}

// MiscCreateResponse struct for misc
// POST: /bool/register
type MiscCreateResponse Misc

// TaskInstancesRequest struct for task
// GET: /tasks
type TaskInstancesRequest struct {
Expand Down
16 changes: 16 additions & 0 deletions _example/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package taskyapi

import "github.com/lestrrat-go/jsval"

var MiscCreateValidator *jsval.JSVal
var TaskCreateValidator *jsval.JSVal
var TaskInstancesValidator *jsval.JSVal
var TaskSelfValidator *jsval.JSVal
Expand All @@ -22,6 +23,21 @@ func init() {
R1 = jsval.String()
M.SetReference("#/definitions/task/definitions/tags", R0)
M.SetReference("#/definitions/task/definitions/title", R1)
MiscCreateValidator = jsval.New().
SetName("MiscCreateValidator").
SetConstraintMap(M).
SetRoot(
jsval.Object().
Required("bool").
AdditionalProperties(
jsval.EmptyConstraint,
).
AddProp(
"bool",
jsval.Boolean(),
),
)

TaskCreateValidator = jsval.New().
SetName("TaskCreateValidator").
SetConstraintMap(M).
Expand Down
43 changes: 33 additions & 10 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func sortValidator(vals []*jsval.JSVal) []*jsval.JSVal {
}

// NewProperty new property
func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema.Schema, method string) (*Property, error) {
func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema.Schema, method string, schemaRequired bool) (*Property, error) {
// save reference before resolving ref
ref := tp.Reference
fieldSchema, err := resolveSchema(tp, root)
Expand All @@ -116,7 +116,7 @@ func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema
Name: name,
Format: string(fieldSchema.Format),
Types: fieldSchema.Type,
Required: df.IsPropRequired(name),
Required: df.IsPropRequired(name) || schemaRequired,
Pattern: fieldSchema.Pattern,
Reference: ref,
Schema: fieldSchema,
Expand Down Expand Up @@ -145,7 +145,7 @@ func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema
// log.Printf("inline obj: %s: %v", name, fieldSchema.Properties)
var inlineFields []*Property
for k, prop := range fieldSchema.Properties {
f, err := NewProperty(k, prop, df, root, method)
f, err := NewProperty(k, prop, df, root, method, false)
if err != nil {
return nil, errors.Wrapf(err, "failed to perse inline object: %s", k)
}
Expand All @@ -167,7 +167,7 @@ func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema
// log.Printf("resolved inline obj: %s: %v", name, item.Properties)
var inlineFields []*Property
for k, prop := range item.Properties {
f, err := NewProperty(k, prop, df, root, method)
f, err := NewProperty(k, prop, df, root, method, false)
if err != nil {
return nil, errors.Wrapf(err, "failed to perse inline object: %s", k)
}
Expand All @@ -179,7 +179,7 @@ func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema
// log.Printf("resolved inline obj: %s: %v", name, resolvedItem.Properties)
var inlineFields []*Property
for k, prop := range resolvedItem.Properties {
f, err := NewProperty(k, prop, df, root, method)
f, err := NewProperty(k, prop, df, root, method, false)
if err != nil {
return nil, errors.Wrapf(err, "failed to perse inline object: %s", k)
}
Expand All @@ -196,7 +196,7 @@ func NewProperty(name string, tp *schema.Schema, df *schema.Schema, root *schema
// inline object without definitions
var inlineFields []*Property
for k, prop := range fieldSchema.Properties {
f, err := NewProperty(k, prop, df, root, method)
f, err := NewProperty(k, prop, df, root, method, false)
if err != nil {
return nil, errors.Wrapf(err, "failed to perse inline object: %s", k)
}
Expand Down Expand Up @@ -248,7 +248,15 @@ func (p *Parser) ParseResources() (map[string]Resource, error) {
// parse resource field
var flds []*Property
for name, tp := range df.Properties {
fld, err := NewProperty(name, tp, df, p.schema, "")
schemaRequired := false
for _, required := range df.Required {
if required == name {
schemaRequired = true
break
}
}

fld, err := NewProperty(name, tp, df, p.schema, "", schemaRequired)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s", id)
}
Expand Down Expand Up @@ -294,7 +302,15 @@ func (p *Parser) ParseActions(res map[string]Resource) (map[string][]Action, err
if e.Schema != nil {
var flds []*Property
for name, tp := range e.Schema.Properties {
fld, err := NewProperty(name, tp, df, p.schema, e.Method)
schemaRequired := false
for _, required := range e.Schema.Required {
if required == name {
schemaRequired = true
break
}
}

fld, err := NewProperty(name, tp, df, p.schema, e.Method, schemaRequired)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s", id)
}
Expand All @@ -314,7 +330,14 @@ func (p *Parser) ParseActions(res map[string]Resource) (map[string][]Action, err
case e.TargetSchema.Reference == "":
var flds []*Property
for name, tp := range e.TargetSchema.Properties {
fld, err := NewProperty(name, tp, df, p.schema, e.Method)
schemaRequired := false
for _, required := range e.TargetSchema.Required {
if required == name {
schemaRequired = true
break
}
}
fld, err := NewProperty(name, tp, df, p.schema, e.Method, schemaRequired)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s", id)
}
Expand All @@ -335,7 +358,7 @@ func (p *Parser) ParseActions(res map[string]Resource) (map[string][]Action, err
IsPrimary: false,
}
case e.TargetSchema.Reference != "" && !IsRefToMainResource(e.TargetSchema.Reference):
fld, err := NewProperty(e.TargetSchema.ID, e.TargetSchema, df, p.schema, e.Method)
fld, err := NewProperty(e.TargetSchema.ID, e.TargetSchema, df, p.schema, e.Method, false)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s", id)
}
Expand Down
Loading
Loading