-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschema_validator.go
More file actions
130 lines (102 loc) · 3.43 KB
/
schema_validator.go
File metadata and controls
130 lines (102 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package statepro
import (
_ "embed"
"encoding/json"
"fmt"
"sync"
"github.com/rendis/statepro/v3/theoretical"
"github.com/santhosh-tekuri/jsonschema/v6"
)
const quantumMachineSchemaResource = "statepro://schema/quantum-machine.schema.json"
var (
//go:embed schema/quantum-machine.schema.json
quantumMachineSchemaBytes []byte
quantumMachineSchemaOnce sync.Once
quantumMachineSchema *jsonschema.Schema
quantumMachineSchemaErr error
)
// QuantumMachineJSONSchema returns the JSON Schema used to validate state machine definitions.
func QuantumMachineJSONSchema() []byte {
return append([]byte(nil), quantumMachineSchemaBytes...)
}
// ValidateQuantumMachineBySchema validates the model using the embedded JSON Schema.
func ValidateQuantumMachineBySchema(source *theoretical.QuantumMachineModel) error {
if source == nil {
return fmt.Errorf("source model cannot be nil")
}
m, err := SerializeQuantumMachineToMap(source)
if err != nil {
return fmt.Errorf("error serializing model: %w", err)
}
return ValidateQuantumMachineBySchemaFromMap(m)
}
// ValidateQuantumMachineBySchemaFromMap validates a map definition using the embedded JSON Schema.
func ValidateQuantumMachineBySchemaFromMap(source map[string]any) error {
if source == nil {
return fmt.Errorf("source map cannot be nil")
}
return validateQuantumMachineBySchema(source)
}
// ValidateQuantumMachineBySchemaFromBinary validates a JSON definition using the embedded JSON Schema.
func ValidateQuantumMachineBySchemaFromBinary(source []byte) error {
if len(source) == 0 {
return fmt.Errorf("source payload cannot be empty")
}
var payload any
if err := json.Unmarshal(source, &payload); err != nil {
return fmt.Errorf("invalid json payload: %w", err)
}
if looksLikeJSONSchemaDocument(payload) {
return fmt.Errorf(
"payload appears to be a JSON Schema document, not a QuantumMachineModel instance; " +
"use a JSON Schema meta-schema (for example draft 2020-12) to validate schema documents",
)
}
return validateQuantumMachineBySchema(payload)
}
func validateQuantumMachineBySchema(payload any) error {
schema, err := getQuantumMachineSchema()
if err != nil {
return fmt.Errorf("error loading quantum machine schema: %w", err)
}
if err = schema.Validate(payload); err != nil {
return fmt.Errorf("json schema validation failed: %w", err)
}
return nil
}
func getQuantumMachineSchema() (*jsonschema.Schema, error) {
quantumMachineSchemaOnce.Do(func() {
compiler := jsonschema.NewCompiler()
var schemaDocument any
if err := json.Unmarshal(quantumMachineSchemaBytes, &schemaDocument); err != nil {
quantumMachineSchemaErr = err
return
}
if err := compiler.AddResource(quantumMachineSchemaResource, schemaDocument); err != nil {
quantumMachineSchemaErr = err
return
}
quantumMachineSchema, quantumMachineSchemaErr = compiler.Compile(quantumMachineSchemaResource)
})
if quantumMachineSchemaErr != nil {
return nil, quantumMachineSchemaErr
}
return quantumMachineSchema, nil
}
func looksLikeJSONSchemaDocument(payload any) bool {
m, ok := payload.(map[string]any)
if !ok {
return false
}
_, hasSchema := m["$schema"]
_, hasDefs := m["$defs"]
_, hasProperties := m["properties"]
_, hasID := m["id"]
_, hasCanonicalName := m["canonicalName"]
_, hasUniverses := m["universes"]
_, hasInitials := m["initials"]
if hasID || hasCanonicalName || hasUniverses || hasInitials {
return false
}
return hasSchema && (hasDefs || hasProperties)
}