Files
shields/core/base-service/transform-example.js
Pierre-Yves B bc96f0e25f Example keywords validation (#2956)
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.
2019-02-13 13:14:12 -04:00

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,
}