diff --git a/_example/doc/schema/schema.json b/_example/doc/schema/schema.json
index f9e2358..f285cd6 100644
--- a/_example/doc/schema/schema.json
+++ b/_example/doc/schema/schema.json
@@ -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",
@@ -353,6 +423,9 @@
"error": {
"$ref": "#/definitions/error"
},
+ "misc": {
+ "$ref": "#/definitions/misc"
+ },
"task": {
"$ref": "#/definitions/task"
},
diff --git a/_example/doc/schema/schema.md b/_example/doc/schema/schema.md
index 204a75b..da82d0f 100644
--- a/_example/doc/schema/schema.md
+++ b/_example/doc/schema/schema.md
@@ -24,6 +24,8 @@ Tasky-App-Version 1.0.0
## The table of contents
- Error
+- Misc
+ - POST /bool/register
- Task
- GET /tasks/{task_id}
- POST /tasks
@@ -47,6 +49,59 @@ This resource represents API error
| **errorFields/name** | *string* | param field name | `"status"` |
+## Misc
+
+Stability: `prototype`
+
+Misc
+
+### Attributes
+
+| Name | Type | Description | Example |
+| ------- | ------- | ------- | ------- |
+| **[id](#resource-user)** | *uuid* | user id | `"ec0a1edc-062e-11e7-8b1e-040ccee2aa06"` |
+
+### Misc detail
+
+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
+}
+```
+
+
## Task
Stability: `prototype`
diff --git a/_example/doc/schema/schemata/misc.yml b/_example/doc/schema/schemata/misc.yml
new file mode 100644
index 0000000..e81d40a
--- /dev/null
+++ b/_example/doc/schema/schemata/misc.yml
@@ -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
diff --git a/_example/struct.go b/_example/struct.go
index bc915fc..4eb9acf 100644
--- a/_example/struct.go
+++ b/_example/struct.go
@@ -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"`
@@ -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 {
diff --git a/_example/validator.go b/_example/validator.go
index 230c0f8..7a376b8 100644
--- a/_example/validator.go
+++ b/_example/validator.go
@@ -1,7 +1,8 @@
package taskyapi
-import jsval "github.com/lestrrat-go/go-jsval"
+import "github.com/lestrrat-go/jsval"
+var MiscCreateValidator *jsval.JSVal
var TaskCreateValidator *jsval.JSVal
var TaskInstancesValidator *jsval.JSVal
var TaskSelfValidator *jsval.JSVal
@@ -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).
diff --git a/parser.go b/parser.go
index ac23da2..9e6e93c 100644
--- a/parser.go
+++ b/parser.go
@@ -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)
@@ -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,
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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)
}
diff --git a/parser_test.go b/parser_test.go
index d2387b5..4586a24 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -68,3 +68,37 @@ func TestParseActions(t *testing.T) {
}
}
}
+
+func TestParseActions_SchemaRequired(t *testing.T) {
+ parser := testNewParser(t)
+ r, err := parser.ParseResources()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ res, err := parser.ParseActions(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ a := res["misc"][0]
+ if a.Href != "/bool/register" {
+ t.Fatalf("href is not /bool/register, %s", a.Href)
+ }
+
+ name := a.Request.Properties[0]
+ if name.Name != "bool" {
+ t.Fatalf("not a target: %s", name.Name)
+ }
+ if !name.Required {
+ t.Fatal("bool is required")
+ }
+
+ isTrue := a.Response.Properties[1]
+ if isTrue.Name != "isTrue" {
+ t.Fatalf("not a target: %s", isTrue.Name)
+ }
+ if !isTrue.Required {
+ t.Fatal("isTrue is required")
+ }
+}