This pull request closes #2551: making sure that the keywords don't already appear in the example's title. I also added validation that checks that they are at least two characters long, as this is enforced by the homepage when type your search.
170 lines
4.2 KiB
JavaScript
170 lines
4.2 KiB
JavaScript
'use strict'
|
|
|
|
const Joi = require('joi')
|
|
const pathToRegexp = require('path-to-regexp')
|
|
const coalesceBadge = require('./coalesce-badge')
|
|
const { makeFullUrl } = require('./route')
|
|
|
|
const optionalObjectOfKeyValues = Joi.object().pattern(
|
|
/./,
|
|
Joi.string().allow(null)
|
|
)
|
|
|
|
const schema = Joi.object({
|
|
// This should be:
|
|
// title: Joi.string().required(),
|
|
title: Joi.string(),
|
|
namedParams: optionalObjectOfKeyValues.required(),
|
|
queryParams: optionalObjectOfKeyValues.default({}),
|
|
pattern: Joi.string(),
|
|
staticPreview: Joi.object({
|
|
label: Joi.string(),
|
|
message: Joi.alternatives()
|
|
.try(
|
|
Joi.string()
|
|
.allow('')
|
|
.required(),
|
|
Joi.number()
|
|
)
|
|
.required(),
|
|
color: Joi.string(),
|
|
style: Joi.string(),
|
|
}).required(),
|
|
keywords: Joi.array()
|
|
.items(Joi.string())
|
|
.default([]),
|
|
documentation: Joi.string(), // Valid HTML.
|
|
}).required()
|
|
|
|
function validateExample(example, index, ServiceClass) {
|
|
const result = Joi.attempt(
|
|
example,
|
|
schema,
|
|
`Example for ${ServiceClass.name} at index ${index}`
|
|
)
|
|
|
|
const { pattern, namedParams } = result
|
|
|
|
if (!pattern && !ServiceClass.route.pattern) {
|
|
throw new Error(
|
|
`Example for ${
|
|
ServiceClass.name
|
|
} at index ${index} does not declare a pattern`
|
|
)
|
|
}
|
|
if (pattern === ServiceClass.route.pattern) {
|
|
throw new Error(
|
|
`Example for ${
|
|
ServiceClass.name
|
|
} at index ${index} declares a redundant pattern which should be removed`
|
|
)
|
|
}
|
|
|
|
// Make sure we can build the full URL using these patterns.
|
|
try {
|
|
pathToRegexp.compile(pattern || ServiceClass.route.pattern)(namedParams)
|
|
} catch (e) {
|
|
throw Error(
|
|
`In example for ${
|
|
ServiceClass.name
|
|
} at index ${index}, ${e.message.toLowerCase()}`
|
|
)
|
|
}
|
|
// Make sure there are no extra keys.
|
|
let keys = []
|
|
pathToRegexp(pattern || ServiceClass.route.pattern, keys)
|
|
keys = keys.map(({ name }) => name)
|
|
const extraKeys = Object.keys(namedParams).filter(k => !keys.includes(k))
|
|
if (extraKeys.length) {
|
|
throw Error(
|
|
`In example for ${
|
|
ServiceClass.name
|
|
} at index ${index}, namedParams contains unknown keys: ${extraKeys.join(
|
|
', '
|
|
)}`
|
|
)
|
|
}
|
|
|
|
if (example.keywords) {
|
|
// Make sure the keywords are at least two characters long.
|
|
const tinyKeywords = example.keywords.filter(k => k.length < 2)
|
|
if (tinyKeywords.length) {
|
|
throw Error(
|
|
`In example for ${
|
|
ServiceClass.name
|
|
} at index ${index}, keywords contains words that are less than two characters long: ${tinyKeywords.join(
|
|
', '
|
|
)}`
|
|
)
|
|
}
|
|
// Make sure none of the keywords are already included in the title.
|
|
const title = (example.title || ServiceClass.name).toLowerCase()
|
|
const redundantKeywords = example.keywords.filter(k =>
|
|
title.includes(k.toLowerCase())
|
|
)
|
|
if (redundantKeywords.length) {
|
|
throw Error(
|
|
`In example for ${
|
|
ServiceClass.name
|
|
} at index ${index}, keywords contains words that are already in the title: ${redundantKeywords.join(
|
|
', '
|
|
)}`
|
|
)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
function transformExample(inExample, index, ServiceClass) {
|
|
const {
|
|
// We should get rid of this transform, since the class name is never what
|
|
// we want to see.
|
|
title = ServiceClass.name,
|
|
namedParams,
|
|
queryParams,
|
|
pattern,
|
|
staticPreview,
|
|
keywords,
|
|
documentation,
|
|
} = validateExample(inExample, index, ServiceClass)
|
|
|
|
const {
|
|
text: [label, message],
|
|
color,
|
|
template: style,
|
|
namedLogo,
|
|
} = coalesceBadge(
|
|
{},
|
|
staticPreview,
|
|
ServiceClass.defaultBadgeData,
|
|
ServiceClass
|
|
)
|
|
|
|
return {
|
|
title,
|
|
example: {
|
|
pattern: makeFullUrl(
|
|
ServiceClass.route.base,
|
|
pattern || ServiceClass.route.pattern
|
|
),
|
|
namedParams,
|
|
queryParams,
|
|
},
|
|
preview: {
|
|
label,
|
|
message: `${message}`,
|
|
color,
|
|
style: style === 'flat' ? undefined : style,
|
|
namedLogo,
|
|
},
|
|
keywords,
|
|
documentation: documentation ? { __html: documentation } : undefined,
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
validateExample,
|
|
transformExample,
|
|
}
|