1 of 59

Schema Negotiation

Paul Frazee // Bluesky

2 of 59

3 of 59

TCP

HTTP

JSON

???

4 of 59

TCP

HTTP

JSON

???

???

5 of 59

FLEXIBLE

CORRECT

6 of 59

7 of 59

8 of 59

9 of 59

10 of 59

11 of 59

INTENT

RESULT

12 of 59

INTENT

RESULT

VIOLATION

13 of 59

{

"type": "tweet",

"media": ["sock.jpg"]

}

14 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"status": "picture-of-the-day"

}

15 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"status": "picture-of-the-day"

}

16 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"status": "picture-of-the-day"

}

INTENT

17 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"category": "sock-pictures"

}

INTENT

18 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"contentWarning": "dirty-socks"

}

INTENT

19 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"to": "bob@mail.com",

"cc": "sock-lovers@mail.com"

}

INTENT

20 of 59

{

"type": "tweet",

"media": ["sock.jpg"],

"to": "bob@mail.com",

"cc": "sock-lovers@mail.com"

}

INTENT

CONTRACT

21 of 59

1

Schema is a contract

22 of 59

interface Post {

text: string,

media: string[]

}

23 of 59

interface Post {

text: string,

media: string[]

}

V1

24 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media: MediaEmbed[]

}

25 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media: MediaEmbed[]

}

V2

26 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media: MediaEmbed[]

}

V2

27 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media: MediaEmbed[]

}

V2

28 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media:

(string|MediaEmbed)[]

}

V2

29 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media:

(string|MediaEmbed)[]

}

V2

30 of 59

interface Post {

text: string,

media: string[]

}

V1

interface Post {

text: string,

media: string[],

mediaExt?: MediaEmbed[]

}

V2

31 of 59

1

Schema is a contract

2

Minor revisions only

32 of 59

SCHEMA NEGOTIATION

SUPPORTED SCHEMAS

REQUIRED SCHEMAS

Consumer

Provider

33 of 59

34 of 59

I understand

POST

POSTv2

COMMENT

"

"

35 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

36 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

POSTv2

  • StatusUpdates

Base

Type

37 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

POSTv2

  • StatusUpdate

Extension Type

38 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

39 of 59

I understand

POST

POSTv2

COMMENT

StatusUpdate

"

"

{..}

.json

  • StatusUpdate

POSTv2

40 of 59

I understand

POST

POSTv2

COMMENT

StatusUpdate

"

"

{..}

.json

  • StatusUpdate

POSTv2

41 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

42 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

Fallback: IGNORE

43 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

Fallback: WARN

44 of 59

I understand

POST

POSTv2

COMMENT

"

"

{..}

.json

  • StatusUpdate

POSTv2

Fallback: ERROR

45 of 59

SCHEMA NEGOTIATION

SUPPORTED SCHEMAS

REQUIRED SCHEMAS

Consumer

Provider

46 of 59

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

DECLARE SUPPORT

47 of 59

VALIDATE RECORD

res = v.validate({

type: 'Post',

text: 'Hello, world!'

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

48 of 59

SUPPORTED? YES

res = v.validate({

type: 'Post',

text: 'Hello, world!'

})

res.supported == true

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

49 of 59

VALID? YES

res = v.validate({

type: 'Post',

text: 'Hello, world!'

})

res.supported == true

res.valid == true

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

50 of 59

VALID? NO

res = v.validate({

type: 'Post',

text: 12345

})

res.supported == true

res.valid == false

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

51 of 59

SUPPORTED? NO

res = v.validate({

type: 'OtherPost',

text: 'Hello World'

})

res.supported == false

res.valid == undefined

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

52 of 59

EXTENSION

res = v.validate({

type: 'Post',

text: 'Hello World',

ext: {

type: 'StatusUpdate',

value: 'picture-of-the-day'

}

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

53 of 59

SUPPORTED? YES

res = v.validate({

type: 'Post',

text: 'Hello World',

ext: {

type: 'StatusUpdate',

value: 'picture-of-the-day'

}

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

54 of 59

SUPPORTED? NO BUT IGNORE

res = v.validate({

type: 'Post',

text: 'Hello World',

ext: {

type: 'Poll',

question: 'A or B?'

}

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

55 of 59

SUPPORTED? NO, WARN

res = v.validate({

type: 'Post',

text: 'Hello World',

ext: {

type: 'Poll',

question: 'A or B?',

fallback: 'This post contains a poll'

}

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

56 of 59

SUPPORTED? NO, FAIL

res = v.validate({

type: 'Post',

text: 'Hello World',

ext: {

type: 'Poll',

question: 'A or B?',

required: true

}

})

v = createValidator({

base: ['Post', 'Post.v2', 'Comment'],

ext: ['StatusUpdate']

})

57 of 59

1

Schema is a contract

2

Minor revisions only

3

Extend by negotiation

58 of 59

(WIP)

59 of 59

Thanks!

Paul Frazee // Bluesky