2014-04-12 04:35:56 -04:00
#language imports
Language = require ' ./languages '
# schema helper methods
me = module . exports
combine = (base, ext) ->
return base unless ext ?
return _ . extend ( base , ext )
2014-06-12 22:30:29 -04:00
urlPattern = ' ^(ht|f)tp(s?) \: \/ \/ [0-9a-zA-Z]([-. \w ]*[0-9a-zA-Z])*(:(0-9)*)*( \/ ?)([a-zA-Z0-9 \- \. \? \, \' \/ \\ \+ &% \$ # _=]*)?$ '
2014-04-12 04:35:56 -04:00
# Common schema properties
2014-06-30 22:16:26 -04:00
me.object = (ext, props) -> combine ( { type: ' object ' , additionalProperties: false , properties: props or { } } , ext )
me.array = (ext, items) -> combine ( { type: ' array ' , items: items or { } } , ext )
2014-04-12 04:35:56 -04:00
me.shortString = (ext) -> combine ( { type: ' string ' , maxLength: 100 } , ext )
me.pct = (ext) -> combine ( { type: ' number ' , maximum: 1.0 , minimum: 0.0 } , ext )
2015-03-07 19:30:25 -05:00
# Dates should usually be strings, ObjectIds should be strings: https://github.com/codecombat/codecombat/issues/1384
2015-03-09 12:30:51 -04:00
me.date = (ext) -> combine ( { type: [ ' object ' , ' string ' ] , format: ' date-time ' } , ext ) # old
me.stringDate = (ext) -> combine ( { type: [ ' string ' ] , format: ' date-time ' } , ext ) # new
2015-03-07 19:30:25 -05:00
me.objectId = (ext) -> schema = combine ( { type: [ ' object ' , ' string ' ] } , ext ) # old
me.stringID = (ext) -> schema = combine ( { type: ' string ' , minLength: 24 , maxLength: 24 } , ext ) # use for anything new
2014-04-12 04:35:56 -04:00
me.url = (ext) -> combine ( { type: ' string ' , format: ' url ' , pattern: urlPattern } , ext )
2014-08-05 08:33:33 -04:00
me.int = (ext) -> combine { type: ' integer ' } , ext
me.float = (ext) -> combine { type: ' number ' } , ext
2014-04-12 04:35:56 -04:00
2014-06-30 22:16:26 -04:00
PointSchema = me . object { title: ' Point ' , description: ' An {x, y} coordinate point. ' , format: ' point2d ' , required: [ ' x ' , ' y ' ] } ,
x: { title: ' x ' , description: ' The x coordinate. ' , type: ' number ' , ' default ' : 15 }
y: { title: ' y ' , description: ' The y coordinate. ' , type: ' number ' , ' default ' : 20 }
2014-04-12 04:35:56 -04:00
me.point2d = (ext) -> combine ( _ . cloneDeep ( PointSchema ) , ext )
2014-06-30 22:16:26 -04:00
SoundSchema = me . object { format: ' sound ' } ,
mp3: { type: ' string ' , format: ' sound-file ' }
ogg: { type: ' string ' , format: ' sound-file ' }
2014-04-12 04:35:56 -04:00
me.sound = (props) ->
obj = _ . cloneDeep ( SoundSchema )
obj . properties [ prop ] = props [ prop ] for prop of props
obj
2014-06-30 22:16:26 -04:00
ColorConfigSchema = me . object { format: ' color-sound ' } ,
hue: { format: ' range ' , type: ' number ' , minimum: 0 , maximum: 1 }
saturation: { format: ' range ' , type: ' number ' , minimum: 0 , maximum: 1 }
lightness: { format: ' range ' , type: ' number ' , minimum: 0 , maximum: 1 }
2014-04-12 04:35:56 -04:00
me.colorConfig = (props) ->
obj = _ . cloneDeep ( ColorConfigSchema )
obj . properties [ prop ] = props [ prop ] for prop of props
obj
# BASICS
basicProps = (linkFragment) ->
2014-06-30 22:16:26 -04:00
_id: me . objectId ( links: [ { rel: ' self ' , href: " /db/ #{ linkFragment } /{($)} " } ] , format: ' hidden ' )
__v: { title: ' Mongoose Version ' , format: ' hidden ' }
2014-04-12 04:35:56 -04:00
me.extendBasicProperties = (schema, linkFragment) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , basicProps ( linkFragment ) )
2014-06-10 19:30:07 -04:00
2014-04-12 04:46:41 -04:00
# PATCHABLE
patchableProps = ->
patches: me . array ( { title : ' Patches ' } , {
2014-06-30 22:16:26 -04:00
_id: me . objectId ( links: [ { rel: ' db ' , href: ' /db/patch/{($)} ' } ] , title: ' Patch ID ' , description: ' A reference to the patch. ' )
status: { enum: [ ' pending ' , ' accepted ' , ' rejected ' , ' cancelled ' ] }
2014-04-12 04:46:41 -04:00
} )
2014-06-30 22:16:26 -04:00
allowPatches: { type: ' boolean ' }
watchers: me . array ( { title: ' Watchers ' } ,
me . objectId ( links: [ { rel: ' extra ' , href: ' /db/user/{($)} ' } ] ) )
2014-06-10 19:30:07 -04:00
2014-04-12 04:46:41 -04:00
me.extendPatchableProperties = (schema) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , patchableProps ( ) )
2014-04-12 04:35:56 -04:00
# NAMED
namedProps = ->
name: me . shortString ( { title: ' Name ' } )
slug: me . shortString ( { title: ' Slug ' , format: ' hidden ' } )
me.extendNamedProperties = (schema) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , namedProps ( ) )
# VERSIONED
versionedProps = (linkFragment) ->
version:
2014-06-30 22:16:26 -04:00
' default ' : { minor: 0 , major: 0 , isLatestMajor: true , isLatestMinor: true }
2014-04-12 04:35:56 -04:00
format: ' version '
title: ' Version '
type: ' object '
readOnly: true
additionalProperties: false
properties:
2014-06-30 22:16:26 -04:00
major: { type: ' number ' , minimum: 0 }
minor: { type: ' number ' , minimum: 0 }
isLatestMajor: { type: ' boolean ' }
isLatestMinor: { type: ' boolean ' }
2014-04-12 04:35:56 -04:00
# TODO: figure out useful 'rel' values here
original: me . objectId ( links: [ { rel: ' extra ' , href: " /db/ #{ linkFragment } /{($)} " } ] , format: ' hidden ' )
parent: me . objectId ( links: [ { rel: ' extra ' , href: " /db/ #{ linkFragment } /{($)} " } ] , format: ' hidden ' )
2014-06-30 22:16:26 -04:00
creator: me . objectId ( links: [ { rel: ' extra ' , href: ' /db/user/{($)} ' } ] , format: ' hidden ' )
created: me . date ( { title: ' Created ' , readOnly: true } )
commitMessage: { type: ' string ' , maxLength: 500 , title: ' Commit Message ' , readOnly: true }
2014-04-12 04:35:56 -04:00
me.extendVersionedProperties = (schema, linkFragment) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , versionedProps ( linkFragment ) )
# SEARCHABLE
searchableProps = ->
2014-06-30 22:16:26 -04:00
index: { format: ' hidden ' }
2014-04-12 04:35:56 -04:00
me.extendSearchableProperties = (schema) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , searchableProps ( ) )
# PERMISSIONED
permissionsProps = ->
permissions:
type: ' array '
items:
type: ' object '
additionalProperties: false
properties:
target: { }
access: { type: ' string ' , ' enum ' : [ ' read ' , ' write ' , ' owner ' ] }
2014-06-30 22:16:26 -04:00
format: ' hidden '
2014-04-12 04:35:56 -04:00
me.extendPermissionsProperties = (schema) ->
schema.properties = { } unless schema . properties ?
_ . extend ( schema . properties , permissionsProps ( ) )
# TRANSLATABLE
2014-06-30 22:16:26 -04:00
me.generateLanguageCodeArrayRegex = -> ' ^( ' + Language . languageCodes . join ( ' | ' ) + ' )$ '
2014-04-12 04:35:56 -04:00
me.getLanguageCodeArray = ->
return Language . languageCodes
me.getLanguagesObject = -> return Language
2014-12-10 13:00:52 -05:00
2014-10-17 12:12:06 -04:00
me.extendTranslationCoverageProperties = (schema) ->
schema.properties = { } unless schema . properties ?
schema.properties.i18nCoverage = { title: ' i18n Coverage ' , type: ' array ' , items: { type: ' string ' } }
2014-04-12 04:35:56 -04:00
# OTHER
2014-06-30 22:16:26 -04:00
me.classNamePattern = ' ^[A-Z][A-Za-z0-9]*$ ' # starts with capital letter; just letters and numbers
me.identifierPattern = ' ^[a-z][A-Za-z0-9]*$ ' # starts with lowercase letter; just letters and numbers
me.constantPattern = ' ^[A-Z0-9_]+$ ' # just uppercase letters, underscores, and numbers
me.identifierOrConstantPattern = ' ^([a-z][A-Za-z0-9]*|[A-Z0-9_]+)$ '
2014-04-12 04:35:56 -04:00
me.FunctionArgumentSchema = me . object {
2014-06-30 22:16:26 -04:00
title: ' Function Argument ' ,
description: ' Documentation entry for a function argument. '
' default ' :
name: ' target '
type: ' object '
example: ' this.getNearestEnemy() '
description: ' The target of this function. '
2014-04-12 04:35:56 -04:00
required: [ ' name ' , ' type ' , ' example ' , ' description ' ]
} ,
2014-06-30 22:16:26 -04:00
name: { type: ' string ' , pattern: me . identifierPattern , title: ' Name ' , description: ' Name of the function argument. ' }
2014-10-27 20:11:48 -04:00
i18n: { type: ' object ' , format: ' i18n ' , props: [ ' description ' ] , description: ' Help translate this argument ' }
2014-04-12 04:35:56 -04:00
# not actual JS types, just whatever they describe...
2014-06-30 22:16:26 -04:00
type: me . shortString ( title: ' Type ' , description: ' Intended type of the argument. ' )
2014-06-26 01:56:39 -04:00
example:
oneOf: [
{
type: ' object ' ,
2014-06-30 22:16:26 -04:00
title: ' Language Examples ' ,
description: ' Examples by code language. ' ,
2014-06-26 01:56:39 -04:00
additionalProperties: me . shortString ( description: ' Example value for the argument. ' )
2014-07-10 11:31:34 -04:00
format: ' code-languages-object '
2014-09-25 16:17:41 -04:00
default: { javascript: ' ' , python: ' ' }
2014-06-26 01:56:39 -04:00
}
2014-07-10 11:31:34 -04:00
me . shortString ( title: ' Example ' , description: ' Example value for the argument. ' )
2014-06-26 01:56:39 -04:00
]
description:
oneOf: [
{
type: ' object ' ,
2014-06-30 22:16:26 -04:00
title: ' Language Descriptions ' ,
description: ' Example argument descriptions by code language. ' ,
additionalProperties: { type: ' string ' , description: ' Description of the argument. ' , maxLength: 1000 }
2014-07-10 11:31:34 -04:00
format: ' code-languages-object '
2014-09-25 16:17:41 -04:00
default: { javascript: ' ' , python: ' ' }
2014-06-26 01:56:39 -04:00
}
2014-07-10 11:31:34 -04:00
{ title: ' Description ' , type: ' string ' , description: ' Description of the argument. ' , maxLength: 1000 }
2014-06-26 01:56:39 -04:00
]
2014-06-30 22:16:26 -04:00
' default ' :
title: ' Default '
description: ' Default value of the argument. (Your code should set this.) '
' default ' : null
2014-06-13 11:12:55 -04:00
2014-07-10 11:31:34 -04:00
me.codeSnippet = me . object { description: ' A language-specific code snippet ' } ,
2014-10-24 13:17:57 -04:00
code: { type: ' string ' , format: ' code ' , title: ' Snippet ' , default: ' ' , description: ' Code snippet. Use ${1:defaultValue} syntax to add flexible arguments ' }
2014-07-09 14:59:21 -04:00
tab: { type: ' string ' , title: ' Tab Trigger ' , description: ' Tab completion text. Will be expanded to the snippet if typed and hit tab. ' }
2014-06-13 11:14:31 -04:00
2014-06-30 22:16:26 -04:00
me.activity = me . object { description: ' Stats on an activity ' } ,
2014-06-10 19:30:07 -04:00
first: me . date ( )
last: me . date ( )
count: { type: ' integer ' , minimum: 0 }
2014-09-02 17:16:36 -04:00
2015-06-08 16:20:54 -04:00
me.terrainString = me . shortString { enum: [ ' Grass ' , ' Dungeon ' , ' Indoor ' , ' Desert ' , ' Mountain ' , ' Glacier ' , ' Volcano ' ] , title: ' Terrain ' , description: ' Which terrain type this is. ' }
2014-09-20 18:18:21 -04:00
me.HeroConfigSchema = me . object { description: ' Which hero the player is using, equipped with what inventory. ' } ,
inventory:
type: ' object '
description: ' The inventory of the hero: slots to item ThangTypes. '
additionalProperties: me . objectId ( description: ' An item ThangType. ' )
thangType: me . objectId ( links: [ { rel: ' db ' , href: ' /db/thang.type/{($)}/version ' } ] , title: ' Thang Type ' , description: ' The ThangType of the hero. ' , format: ' thang-type ' )
2014-09-26 05:28:54 -04:00
me.RewardSchema = (descriptionFragment='earned by achievements') ->
type: ' object '
additionalProperties: false
description: " Rewards #{ descriptionFragment } . "
properties:
heroes: me . array { uniqueItems: true , description: " Heroes #{ descriptionFragment } . " } ,
me . stringID ( links: [ { rel: ' db ' , href: ' /db/thang.type/{($)}/version ' } ] , title: ' Hero ThangType ' , description: ' A reference to the earned hero ThangType. ' , format: ' thang-type ' )
items: me . array { uniqueItems: true , description: " Items #{ descriptionFragment } . " } ,
me . stringID ( links: [ { rel: ' db ' , href: ' /db/thang.type/{($)}/version ' } ] , title: ' Item ThangType ' , description: ' A reference to the earned item ThangType. ' , format: ' thang-type ' )
levels: me . array { uniqueItems: true , description: " Levels #{ descriptionFragment } . " } ,
me . stringID ( links: [ { rel: ' db ' , href: ' /db/level/{($)}/version ' } ] , title: ' Level ' , description: ' A reference to the earned Level. ' , format: ' latest-version-original-reference ' )
2015-01-07 15:25:31 -05:00
gems: me . float { description: " Gems #{ descriptionFragment } . " }
2014-12-20 23:01:07 -05:00
me.task = me . object { title: ' Task ' , description: ' A task to be completed ' , format: ' task ' , default: { name: ' TODO ' , complete: false } } ,
name: { title: ' Name ' , description: ' What must be done? ' , type: ' string ' }
complete: { title: ' Complete ' , description: ' Whether this task is done. ' , type: ' boolean ' , format: ' checkbox ' }