Slide 1

Slide 1 text

JSON Schema and APItizer @mihaelkonjevic

Slide 2

Slide 2 text

JSON schema • IETF draft - v4 • JSON based format for defining the structure of JSON data • More info at http://json-schema.org

Slide 3

Slide 3 text

What is a schema? • XML Schema, Relax NG • Set of constraints that have to be satisfied to consider an object valid! • Declarative format for “describing the structure of other data”

Slide 4

Slide 4 text

Why? { "name": "George Washington", "birthday": "February 22, 1732", "address": "Mount Vernon, Virginia, United States" } ! ! ! ! ! { "first_name": "George", "last_name": "Washington", "birthday": "22-02-1732", "address": { "street_address": "3200 Mount Vernon Memorial Highway", "city": "Mount Vernon", "state": "Virginia", "country": "United States" } } VS

Slide 5

Slide 5 text

Schema { "type": "object", "properties": { "first_name": { "type": "string" }, "last_name": { "type": "string" }, "birthday": { "type": "string", "format": "date-time" }, "address": { "type": "object", "properties": { "street_address": { "type": "string" }, "city": { "type": "string" }, "state": { "type": "string" }, "country": { "type" : "string" } } } } } !

Slide 6

Slide 6 text

Validation • Two steps validation: 1. Structural 2. Semantic (Business logic)

Slide 7

Slide 7 text

Validation { "type" : "string", "minLength" : 6, "maxLength" : 255 } password === passwordConfirmation Structural Semantic ✓ ×

Slide 8

Slide 8 text

Types • String • Integer • Number (integer or decimal) • Array • Object • Null • Boolean

Slide 9

Slide 9 text

Types / String • maxLength • minLength • pattern (regex)

Slide 10

Slide 10 text

Types / String Password: ! • at least 1 uppercase letter • at least 1 number • at least 8 chars long ! { "type" : "string", "minLength" : 8, "maxLength" : 255, "pattern" : "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$" }

Slide 11

Slide 11 text

Types / Integer • minimum • maximum • multipleOf • exclusiveMinimum • exclusiveMaximum

Slide 12

Slide 12 text

Types / Integer Integer between 0 - 99 that is a multiple of 3 ! { "type" : “integer”, "minimum" : 0, "maximum" : 100, "exclusiveMaximum" : true, "multipleOf" : 3 }

Slide 13

Slide 13 text

Types / Object • properties • maxProperties • minProperties • required • additionalProperties (boolean or JSON schema object) • patternProperties

Slide 14

Slide 14 text

Types / Object User object: ! • id is an integer • username is a string • password is a string • username and password are required { "type" : "object", "properties" : { "id" : { "type" : "integer" }, "username" : { "type" : "string" }, "password" : { "type" : "string" } }, "required" : ["username", "password"] }

Slide 15

Slide 15 text

Types / Array • items (array or JSON schema object) • minItems • maxItems • additionalItems (boolean or JSON schema object)

Slide 16

Slide 16 text

Types / Array Array of user objects: ! • id is an integer • username is a string • password is a string • username and password are required { "type" : "array", "items" : { "type" : "object", "properties" : { "id" : { "type" : "integer", }, "username" : { "type" : "string", }, "password" : { "type" : "string", } }, "required" : ["username", "password"] } }

Slide 17

Slide 17 text

Types / Array (Tuple) HTTP error response ! • 1st element is an integer • 2nd element is a string { "type" : "array", "items" : [{ "type" : "integer" }, { "type" : "string" }] } ! // [404, "Not Found"]

Slide 18

Slide 18 text

anyOf Data is valid if it matches any of the provided schemas! ! { "anyOf" : [{ "type" : "string", "minLength" : 6 }, { "type" : "string", "maxLength" : 255 }, { "type" : "integer", "minimum" : 20, "maximum" : 100 }] }

Slide 19

Slide 19 text

allOf Data is valid if it matches all of the provided schemas! ! { "allOf" : [{ "type" : "string", "minLength" : 6 }, { "type" : "string", "maxLength" : 255 }] }

Slide 20

Slide 20 text

oneOf Data is valid if it matches exactly one of the provided! schemas! ! { "oneOf" : [{ "type" : "string", "minLength" : 6 }, { "type" : "integer", "minimum" : 20, "maximum" : 100 }] }

Slide 21

Slide 21 text

not Data is valid if it doesn’t match any of the provided! schemas! ! { "not" : [{ "type" : "string", "minLength" : 6 }, { "type" : "string", "maxLength" : 255 }, { "type" : "integer", "minimum" : 20, "maximum" : 100 }] }

Slide 22

Slide 22 text

Schema references • Reference part of the same schema, another schema or part of the another schema • Uses a simple JSON path expression:
 schemaName#path/to/subschema

Slide 23

Slide 23 text

Schema references { "definitions": { "address": { "type": "object", "properties": { "street_address": { "type": "string" }, "city": { "type": "string" }, "state": { "type": "string" } }, "required": ["street_address", "city", "state"] } }, ! "type": "object", ! "properties": { "billing_address": { "$ref": "#/definitions/address" }, "shipping_address": { "$ref": "#/definitions/address" } } } !

Slide 24

Slide 24 text

APItizer JSON schema based mock objects and APIs http://www.github.com/retro/apitizer

Slide 25

Slide 25 text

APItizer • Reuse JSON schema definitions to generate data • Simple mocking of API fixtures • Extendible and customizable

Slide 26

Slide 26 text

Example var userSchema = { type : "object", properties : { id : { type : "integer" }, username : { type : "string" }, password : { type : "string" } } }; ! apitizer.addSchema('user', userSchema); apitizer.fixture.resource('/users', apitizer.schemaStore('user', 10));

Slide 27

Slide 27 text

Example $.get('/users').then(function(users){ // 10 users returned }) ! $.get('/users/1').then(function(user){ // user with id 1 returned }) ! $.post('/users', { username : 'retro', password : '1337' }).then(function(user){ // user is created and returned }) $.ajax('/users/1', { type : 'put', data : { username : 'retro', password : '1337' } }).then(function(user){ // user is updated and returned }) ! $.ajax('/users/1', { type : 'delete' }).then(function(user){ // user is destroyed })

Slide 28

Slide 28 text

Custom API endpoints var userSchema = { type : "object", properties : { id : { type : "integer" }, username : { type : "string" }, password : { type : "string" } } }; ! apitizer.addSchema('user', userSchema); var store = apitizer.schemaStore('user', 10); ! apitizer.fixture('POST /login', function(urlParams, params){ var users = userStore.db(params) // Search the data in the store's database if(users.count() === 0){ throw {errors: ['Wrong credentials'], status: 401} } else { return users.first(); } }) $.post('/login', { username : 'retro', password : 1338 }).then(function(user){ alert('You logged in!') }, function(error){ alert('Wrong credentials!') });

Slide 29

Slide 29 text

Overriding generators var userSchema = { type : "object", properties : { id : { type : "integer" }, username : { type : "string" }, password : { type : "string" } } }; ! apitizer.addSchema('user', userSchema); apitizer.fixture.resource('/users', apitizer.schemaStore('user', 10, { id : apitizer.types.autoincrement(), username : (function(){ var i = 1; return function(){ return "User " + (i++) } })() }));

Slide 30

Slide 30 text

Related objects var userSchema = { type : "object", properties : { id : { type : "integer" }, username : { type : "string" }, password : { type : "string" } } }; ! var articleSchema = { properties : { title : { type : "string" }, user : { $ref : "user" } } } apitizer.addSchema('user', userSchema); apitizer.addSchema('article', articleSchema); ! var userStore = apitizer.schemaStore('user', 10); ! apitizer.fixture.resource('/user', userStore); ! apitizer.fixture.resource('/article', apitizer.schemaStore('article', 10, { user : userStore.one(); }) );

Slide 31

Slide 31 text

Validation example module('apiTest', { setup : function(){ var userSchema = { type : "object", properties : { id : { type : "integer" }, username : { type : "string" }, password : { type : "string" } } }; ! apitizer.addSchema('user', userSchema); } }) ! asyncTest('Getting a user from the API', function(){ expect(1); $.get('/users/1').then(function(user){ ok(apitizer.validateWithSchema('user', user)) }) })

Slide 32

Slide 32 text

Questions? @mihaelkonjevic http://github.com/retro/apitizer