- Ruby 3.3.4
- ensure the docker container is running
docker-compose up -d - then you can run
docker-compose exec web bundle exec rspecin your terminal to run the tests via the docker container
- ValidationSchema model is the main parent model that builds the schema
- It has_many schema_property_fields
- The
all_ofattribute has been setup as a jsonb column and is serialized viaSchemaSerializer::AllOfDetails
validation_schema = ValidationSchema.create(name: "new_user", title: "New User Signup", description: "Validation schema for signing up new users")
SchemaPropertyFieldString.new(name: "firstName", title: "First Name")
validation_schema.schema_property_fields = [
SchemaPropertyFieldString.new(name: "firstName", title: "First Name", required: true),
SchemaPropertyFieldString.new(name: "lastName", title: "Last Name", required: true),
SchemaPropertyFieldString.new(name: "email", title: "Email", required: true)
]
validation_schema.to_schema_property
>>>
{
"title" => "New User Signup",
"description" => "Validation schema for signing up new users",
"properties" => {
"firstName" => {
"title" => "First Name",
"type" => "string",
},
"lastName" => {
"title" => "Last Name",
"type" => "string",
},
"email" => {
"title" => "Email",
"type" => "string",
}
},
"required" => ["firstName", "lastName", "email"]
}
- Takes one required param
properties, which is an Array of Hashes with type and class_attributes keys. The key param is restricted toIfThenRequiredDetailsorOneOfRequiredDetailsand the class_attributes param is dependant on the type param. So if type param isIfThenRequiredDetailsthen class_attributes is restricted to validSchemaSerializer::IfThenRequiredDetailsparams. - NOTE: If you are constructing and using SchemaSerializer classes as standalone and not attached to a ValidationSchema or SchemaPropertyField, then you need to use string hash keys in the Hashes. This is due to serialization that doesn't occur when accessing outside of ValidationSchema or SchemaPropertyField models. This goes for all SchemaSerializer classes. If testing/using not associatied to parent field_details model then use string hash keys and not symbols.
AllOfDetailsPropsType = {
properties: {
type: "IfThenRequiredDetails" | "OneOfRequiredDetails",
class_attributes: IfThenRequiredDetailsProps | OneOfRequiredDetailsProps
}[]
}
details = SchemaSerializer::AllOfDetails.new(
properties: [
{
"type" => "IfThenRequiredDetails",
"class_attributes" => {
"property_name" => "propertyA",
"property_const" => true,
"then_required" => ["propertyB"],
"else_required" => ["propertyC"]
},
},
{
"type" => "OneOfRequiredDetails",
"class_attributes" => {
"required_properties" => [["propertyD"], ["propertyE", "propertyF"]]
},
}
]
)
details.to_schema_property
>>>>
[
{
"if" => { "properties" => { "propertyA" => { "const" => true } } },
"then" => { "required" => ["propertyB"] },
"else" => { "required" => ["propertyC"] }
},
{
"oneOf" => [
{
"required" => ["propertyD"]
},
{
"required" => ["propertyE", "propertyF"]
}
]
}
]
- Used to construct complex if/then/else required field logic.
- This is used to require different fields based on the outcome of other fields.
- The
property_nameis the name of the schema_property_field you want to depend on and check against. - The
property_constcan be restricted to a TrueClass, FalseClass or String property. This is the value of the schema_property_field named in property_name, to check against. This allows us to construct schemas where one form can be required versus another depending on users input to a boolean or string field, such as a country input i.e. "US" or "CA". - The
then_requiredproperty is an Array of Strings. The strings should be names of schema_property_fields that you want to be required if the entry of property_const results in True. - The
else_requiredproperty is optional and is an Array of Strings. The strings should be names of schema_property_fields that you want to be required if the entry of property_const results in False.
IfThenRequiredDetailsPropsType = {
property_name: string
property_const: string or boolean
then_required: string[]
else_required?: string[]
}
details = SchemaSerializer::IfThenRequiredDetails.new(
property_name: "propertyA",
property_const: true,
then_required: ["propertyB"],
else_required: ["propertyC"]
)
details.to_schema_property
>>>>
{
"if" => { "properties" => { "propertyA" => { "const" => true } } },
"then" => { "required" => ["propertyB"] },
"else" => { "required" => ["propertyC"] }
}
In this above scenario propertyA is a Boolean property field i.e. SchemaPropertyFieldBoolean.new(name: "propertyA")
- The
required_propertiesparam is required Array of Array Strings.
OneOfRequiredDetailsPropsType = {
required_properties: string[][]
}
details = SchemaSerializer::OneOfRequiredDetails.new(required_properties: [["propertyD"], ["propertyE", "propertyF"]])
details.to_schema_property
>>>>
{
"oneOf" => [
{
"required" => ["propertyD"]
},
{
"required" => ["propertyE", "propertyF"]
}
]
}
- SchemaPropertyField has been established as a single table inheritance with the following models as types:
SchemaPropertyFieldArray, SchemaPropertyFieldBoolean, SchemaPropertyFieldNumber, SchemaPropertyFieldObject, SchemaPropertyFieldString. - The
nameattribute is used to set the property key when building the schema for the ValidationSchema model. - The
titleanddescriptionattributes can be used to add labels and more information to property the fields. - The
field_detailsattribute has been setup as a jsonb column and each model has it's own serializer for validating and loading specific type data.
- Used for properties requiring multiple entries i.e. aliases, address history, education history and employment history.
details = SchemaPropertyFieldArray.new(name: "aliases", title: "Known Aliases", field_details: SchemaSerializer::ArrayDetails.new(min_items: 1, max_items: 5,
item: {
"type" => "SchemaPropertyFieldString",
"class_attributes" => { "name" => "alias"},
"field_details" => { "min_length" => 1 }
}
))
details.to_schema_property
>>>>
{
"title" => "Known Aliases",
"type" => "array",
"minItems" => 1,
"maxItems" => 5,
"items" => {
"minLength" => 1,
"type" => "string"
}
}
- Used to add Array type specific properties to the schema_property_field_array class
minItemsdescribes the minimum number of entries needed for property to be valid.maxItemsdescribes the maximum number of entries allowed for property to be valid.uniqueItemsdescribes the whether or not the entries need to be different or can be alike for property to be valid.itemdescribes the type of items that should be in the array response i.e. strings, numbers, objects.
SchemaSerializer::ArrayDetailsPropsType = {
minItems?: number,
maxItems?: number,
uniqueItems?: boolean
item: {
type: inclusion in SchemaPropertyField types
class_attributes: validated against SchemaPropertyField model minus validation_schema relationship
field_details?: validated against SchemaPropertyField type field_details serializer
}
}
details = SchemaPropertyFieldArray.new(name: "addresses", title: "Address History", field_details: SchemaSerializer::ArrayDetails.new(min_items: 1, max_items: 2, unique_items: true, item: {
type: "SchemaPropertyFieldObject",
class_attributes: { name: "address", description: "Adress", title: "Enter Address" },
field_details: {
properties: [
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "street", description: "Name of street", title: "Street Name", required: true },
field_details: { min_length: 1 }
},
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "city", description: "Name of city", title: "City Name", required: true },
field_details: { min_length: 1 }
}
]
}
}))
details.to_schema_property
>>>>
{
"title" => "Address History",
"type" => "array",
"minItems" => 1,
"maxItems" => 2,
"uniqueItems" => true,
"items" => {
"description" => "Enter your address",
"title" => "Address",
"type" => "object",
"properties" => {
"street" => {
"description" => "Name of street",
"minLength" => 1,
"title" => "Street Name",
"type" => "string"
},
"city" => {
"description" => "Name of city",
"minLength" => 1,
"title" => "City Name",
"type" => "string"
},
},
"required" => ["street", "city"],
}
}
- Used for properties requiring a boolean (true/false) response value.
details = SchemaPropertyFieldBoolean.new(name: "subscribe", title: "Subscribe", description: "Do you wish to subscribe to our newsletter?")
details.to_schema_property
>>>>
{
"title" => "Subscribe",
"description" => "Do you wish to subscribe to our newsletter?",
"type" => "boolean",
}
- Used to add Boolean type specific properties to the schema_property_field_boolean class
constrestricted totrueorfalseis used to restrict a valid response to a specific answer.
SchemaSerializer::BooleanDetailsPropsType = {
const: true | false,
}
details = SchemaPropertyFieldBoolean.new(name: "consentForm", title: "Consent Form", description: "Do you consent to your data being used?", field_details: SchemaSerializer::BooleanDetails.new(const: true))
details.to_schema_property
>>>>
{
"title" => "Consent Form"",
"description" => "Do you consent to your data being used?"
"type" => "boolean",
"const" => true,
}
- In this above example, only a value of
{ consentForm: true }would be considered valid.
- Used for properties requiring a number (integer) response value.
details = SchemaPropertyFieldNumber.new(name: "age", title: "Current Age", description: "Please enter your age")
details.to_schema_property
>>>>
{
"title" => "Current Age",
"description" => "Please enter your age",
"type" => "number",
}
- Used to add Number type specific properties to the schema_property_field_number class
min_valuethe minimum value for a valid response.max_valuethe maximum value for a valid response.
SchemaSerializer::NumberDetailsPropsType = {
min_value?: number,
max_value?: number,
}
details = SchemaPropertyFieldNumber.new(name: "age", title: "Current Age", description: "Please enter your age", field_details: SchemaSerializer::NumberDetails.new(min_value: 21))
details.to_schema_property
>>>>
{
"title" => "Current Age",
"description" => "Please enter your age",
"type" => "number",
"minValue" => 21
}
- In this above example, only a value > 21 would be valid i.e.
{ age: 21 }
- Used to add Object type specific properties to the schema_property_field_object class.
- The
objectproperty is essentially a validation schema within a validation schema. It is made up of a collection of other properties. - The
field_detailsattribute is required for SchemaPropertyFieldObject to be valid. You need those properties added in order to build the schema property block for anobjecttype.
- Used to build the child properties associated to the parent schema property.
SchemaSerializer::ObjectDetailsPropsType {
type: inclusion in SchemaPropertyField types
class_attributes: validated against SchemaPropertyField model minus validation_schema relationship
field_details?: validated against SchemaPropertyField type field_details serializer
all_of?: {
type: "IfThenRequiredDetails" | "OneOfRequiredDetails",
class_attributes: IfThenRequiredDetailsProps | OneOfRequiredDetailsProps
}[]
}
details = SchemaPropertyFieldObject.new(
name: "address",
title: "Address",
description: "Enter your address",
field_details: SchemaSerializer::ObjectDetails.new(properties: [
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "street", required: true },
},
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "city", required: true },
},
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "state", required: true },
},
])
)
details.to_schema_property
>>>>
{
"title" => "Address",
"description" => "Enter your address",
"type" => "object",
"properties" => {
"street" => { "type" => "string" },
"city" => { "type" => "string" },
"state" => { "type" => "string" },
},
"required" => ["street", "city", "state"]
}
- The
objecttypefield_detailscan also accept theall_ofparam same as the ValidationSchema.
details = SchemaPropertyFieldObject.new(
name: "identification",
title: "Identification",
description: "Please provide valid identifying number",
field_details: SchemaSerializer::ObjectDetails.new(
properties: [
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "ssn" },
},
{
type: "SchemaPropertyFieldString",
class_attributes: { name: "passportNumber" },
},
{
type: "SchemaPropertyFieldObject",
class_attributes: { name: "driversLicense" },
},
],
all_of: [
{
"type" => "IfThenRequiredDetails",
"class_attributes" => {
"property_name" => "birthPlace",
"property_const" => "US",
"then_required" => ["ssn"],
"else_required" => ["passportNumber", "driversLicense"]
},
},
]
)
)
details.to_schema_property
>>>>
{
"title" => "Identification",
"description" => "Please provide valid identifying number",
"type" => "object",
"properties" => {
"ssn" => { "type" => "string" },
"passportNumber" => { "type" => "string" },
"driversLicense" => { "type" => "string" },
},
"allOf" => [
{
"if" => { "properties" => { "birthPlace" => { "const" => "US" } } },
"then" => { "required" => ["ssn"] },
"else" => { "required" => ["passportNumber", "driversLicense"] }
}
]
}
- Used for properties requiring a
stringresponse value.
details = SchemaPropertyFieldString.new(name: "firstName", title: "First Name", description: "Please enter your first name")
details.to_schema_property
>>>>
{
"title" => "First Name",
"description" => "Please enter your first name",
"type" => "string",
}
- Used to add String type specific properties to the schema_property_field_string class
constrestricts valid responses to value in this field.patterna string regex pattern to validate the response against.enumrestricts valid response to one of strings in this array.formatrestricts valid reponse to format type.min_lengththe minimum length for a valid response.max_lengththe maximum length for a valid response.
SchemaSerializer::StringDetailsPropsType = {
const?: string,
pattern?: string,
enum?: string[],
format?: "date" | "date-time" | "email" | "uri"
min_length?: number,
max_length?: number,
}
details = SchemaPropertyFieldString.new(name: "zipcode", title: "Zipcode", description: "Please enter your zipcode", field_details: SchemaSerializer::StringDetails.new(pattern: "(^\d{5}$)|(^\d{5}-\d{4}$)"))
details.to_schema_property
>>>>
{
"title" => "Zipcode",
"description" => "Please enter your zipcode",
"type" => "string",
"pattern" => "(^\d{5}$)|(^\d{5}-\d{4}$)"
}
In this above example you would have to provide a US zipcode for this field to be valid i.e. 5 digits or 5 digits dash 4 digits.