mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-30 10:56:53 -05:00
3d705e5d70
Fix link to /teachers/classes (fixes bugquest#20) Fix edit button color/icon (bugquest#23) Fix bugquest#34 Fix password input width (bugquest#33) Center new pasword text Fix teacher password reset endpoint (bugquest#4) Refactor+use NewHomeView logic for user page button (Fixes bugquest#2) Refactor teacher-password-reset endpoint This makes it much easier to prevent collisions with other logic when PUTing new User attributes. Add regression test for converting to teacher account Fix email verified links, require login (fix bugquest#16) Fix me having stale emailVerified value (Fixes bugquest#40) Don't show JoinClassModal to students Add paragraph to JoinClassModal (fixes bugquest#14) Update change-password label text (fixes bugquest#30) Fix prompting for login on Account Settings page (bugquest #10) Show validation errors for teacher password reset (bugquest#36) Show yellow progress dot in My Classes if anyone has started (bugquest#55) Remove confusing text (bugquest#100)
264 lines
11 KiB
CoffeeScript
264 lines
11 KiB
CoffeeScript
#language imports
|
|
Language = require './languages'
|
|
# schema helper methods
|
|
|
|
me = module.exports
|
|
|
|
combine = (base, ext) ->
|
|
return base unless ext?
|
|
return _.extend(base, ext)
|
|
|
|
urlPattern = '^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_=]*)?$'
|
|
|
|
# Common schema properties
|
|
me.object = (ext, props) -> combine({type: 'object', additionalProperties: false, properties: props or {}}, ext)
|
|
me.array = (ext, items) -> combine({type: 'array', items: items or {}}, ext)
|
|
me.shortString = (ext) -> combine({type: 'string', maxLength: 100}, ext)
|
|
me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, ext)
|
|
me.passwordString = {type: 'string', maxLength: 256, minLength: 2, title: 'Password'}
|
|
|
|
# Dates should usually be strings, ObjectIds should be strings: https://github.com/codecombat/codecombat/issues/1384
|
|
me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext) # old
|
|
me.stringDate = (ext) -> combine({type: ['string'], format: 'date-time'}, ext) # new
|
|
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
|
|
|
|
me.url = (ext) -> combine({type: 'string', format: 'url', pattern: urlPattern}, ext)
|
|
me.int = (ext) -> combine {type: 'integer'}, ext
|
|
me.float = (ext) -> combine {type: 'number'}, ext
|
|
|
|
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}
|
|
|
|
me.point2d = (ext) -> combine(_.cloneDeep(PointSchema), ext)
|
|
|
|
SoundSchema = me.object {format: 'sound'},
|
|
mp3: {type: 'string', format: 'sound-file'}
|
|
ogg: {type: 'string', format: 'sound-file'}
|
|
|
|
me.sound = (props) ->
|
|
obj = _.cloneDeep(SoundSchema)
|
|
obj.properties[prop] = props[prop] for prop of props
|
|
obj
|
|
|
|
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}
|
|
|
|
me.colorConfig = (props) ->
|
|
obj = _.cloneDeep(ColorConfigSchema)
|
|
obj.properties[prop] = props[prop] for prop of props
|
|
obj
|
|
|
|
# BASICS
|
|
|
|
basicProps = (linkFragment) ->
|
|
_id: me.objectId(links: [{rel: 'self', href: "/db/#{linkFragment}/{($)}"}], format: 'hidden')
|
|
__v: {title: 'Mongoose Version', format: 'hidden'}
|
|
|
|
me.extendBasicProperties = (schema, linkFragment) ->
|
|
schema.properties = {} unless schema.properties?
|
|
_.extend(schema.properties, basicProps(linkFragment))
|
|
|
|
# PATCHABLE
|
|
|
|
patchableProps = ->
|
|
patches: me.array({title:'Patches'}, {
|
|
_id: me.objectId(links: [{rel: 'db', href: '/db/patch/{($)}'}], title: 'Patch ID', description: 'A reference to the patch.')
|
|
status: {enum: ['pending', 'accepted', 'rejected', 'cancelled']}
|
|
})
|
|
allowPatches: {type: 'boolean'}
|
|
watchers: me.array({title: 'Watchers'},
|
|
me.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}]))
|
|
|
|
me.extendPatchableProperties = (schema) ->
|
|
schema.properties = {} unless schema.properties?
|
|
_.extend(schema.properties, patchableProps())
|
|
|
|
# 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:
|
|
'default': {minor: 0, major: 0, isLatestMajor: true, isLatestMinor: true}
|
|
format: 'version'
|
|
title: 'Version'
|
|
type: 'object'
|
|
readOnly: true
|
|
additionalProperties: false
|
|
properties:
|
|
major: {type: 'number', minimum: 0}
|
|
minor: {type: 'number', minimum: 0}
|
|
isLatestMajor: {type: 'boolean'}
|
|
isLatestMinor: {type: 'boolean'}
|
|
# 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')
|
|
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}
|
|
|
|
me.extendVersionedProperties = (schema, linkFragment) ->
|
|
schema.properties = {} unless schema.properties?
|
|
_.extend(schema.properties, versionedProps(linkFragment))
|
|
|
|
# SEARCHABLE
|
|
|
|
searchableProps = ->
|
|
index: {format: 'hidden'}
|
|
|
|
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']}
|
|
format: 'hidden'
|
|
|
|
me.extendPermissionsProperties = (schema) ->
|
|
schema.properties = {} unless schema.properties?
|
|
_.extend(schema.properties, permissionsProps())
|
|
|
|
# TRANSLATABLE
|
|
|
|
me.generateLanguageCodeArrayRegex = -> '^(' + Language.languageCodes.join('|') + ')$'
|
|
|
|
me.getLanguageCodeArray = ->
|
|
return Language.languageCodes
|
|
|
|
me.getLanguagesObject = -> return Language
|
|
|
|
me.extendTranslationCoverageProperties = (schema) ->
|
|
schema.properties = {} unless schema.properties?
|
|
schema.properties.i18nCoverage = { title: 'i18n Coverage', type: 'array', items: { type: 'string' }}
|
|
|
|
# OTHER
|
|
|
|
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_]+)$'
|
|
|
|
me.FunctionArgumentSchema = me.object {
|
|
title: 'Function Argument',
|
|
description: 'Documentation entry for a function argument.'
|
|
'default':
|
|
name: 'target'
|
|
type: 'object'
|
|
example: 'this.getNearestEnemy()'
|
|
description: 'The target of this function.'
|
|
required: ['name', 'type', 'example', 'description']
|
|
},
|
|
name: {type: 'string', pattern: me.identifierPattern, title: 'Name', description: 'Name of the function argument.'}
|
|
i18n: { type: 'object', format: 'i18n', props: ['description'], description: 'Help translate this argument'}
|
|
# not actual JS types, just whatever they describe...
|
|
type: me.shortString(title: 'Type', description: 'Intended type of the argument.')
|
|
example:
|
|
oneOf: [
|
|
{
|
|
type: 'object',
|
|
title: 'Language Examples',
|
|
description: 'Examples by code language.',
|
|
additionalProperties: me.shortString(description: 'Example value for the argument.')
|
|
format: 'code-languages-object'
|
|
default: {javascript: '', python: ''}
|
|
}
|
|
me.shortString(title: 'Example', description: 'Example value for the argument.')
|
|
]
|
|
description:
|
|
oneOf: [
|
|
{
|
|
type: 'object',
|
|
title: 'Language Descriptions',
|
|
description: 'Example argument descriptions by code language.',
|
|
additionalProperties: {type: 'string', description: 'Description of the argument.', maxLength: 1000}
|
|
format: 'code-languages-object'
|
|
default: {javascript: '', python: ''}
|
|
}
|
|
{title: 'Description', type: 'string', description: 'Description of the argument.', maxLength: 1000}
|
|
]
|
|
'default':
|
|
title: 'Default'
|
|
description: 'Default value of the argument. (Your code should set this.)'
|
|
'default': null
|
|
|
|
me.codeSnippet = me.object {description: 'A language-specific code snippet'},
|
|
code: {type: 'string', format: 'code', title: 'Snippet', default: '', description: 'Code snippet. Use ${1:defaultValue} syntax to add flexible arguments'}
|
|
tab: {type: 'string', title: 'Tab Trigger', description: 'Tab completion text. Will be expanded to the snippet if typed and hit tab.'}
|
|
|
|
me.activity = me.object {description: 'Stats on an activity'},
|
|
first: me.date()
|
|
last: me.date()
|
|
count: {type: 'integer', minimum: 0}
|
|
|
|
me.terrainString = me.shortString {enum: ['Grass', 'Dungeon', 'Indoor', 'Desert', 'Mountain', 'Glacier', 'Volcano'], title: 'Terrain', description: 'Which terrain type this is.'}
|
|
|
|
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')
|
|
|
|
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')
|
|
gems: me.float {description: "Gems #{descriptionFragment}."}
|
|
|
|
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'}
|
|
|
|
me.concept = me.shortString enum: [
|
|
'advanced_strings'
|
|
'algorithms'
|
|
'arguments'
|
|
'arithmetic'
|
|
'arrays'
|
|
'basic_syntax'
|
|
'boolean_logic'
|
|
'break_statements'
|
|
'classes'
|
|
'continue_statements'
|
|
'for_loops'
|
|
'functions'
|
|
'graphics'
|
|
'if_statements'
|
|
'input_handling'
|
|
'math_operations'
|
|
'object_literals'
|
|
'parameters'
|
|
'strings'
|
|
'variables'
|
|
'vectors'
|
|
'while_loops'
|
|
'recursion'
|
|
]
|