Add frontend prop-type validation for the service definition schema (#3063)
Closes #2702
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
|
||||
const Joi = require('joi')
|
||||
|
||||
// This should be kept in sync with the schema in
|
||||
// `frontend/lib/service-definitions/service-definition-prop-types.js`.
|
||||
|
||||
const arrayOfStrings = Joi.array()
|
||||
.items(Joi.string())
|
||||
.allow([])
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
badgeUrlFromPath,
|
||||
staticBadgeUrl,
|
||||
} from '../../core/badge-urls/make-badge-url'
|
||||
import { serviceDefinitionPropType } from '../lib/service-definitions/service-definition-prop-types'
|
||||
import { Badge } from './common'
|
||||
import { StyledCode } from './snippet'
|
||||
|
||||
@@ -28,7 +29,7 @@ const ClickableCode = styled(StyledCode)`
|
||||
|
||||
export default class BadgeExamples extends React.Component {
|
||||
static propTypes = {
|
||||
definitions: PropTypes.array.isRequired,
|
||||
definitions: PropTypes.arrayOf(serviceDefinitionPropType).isRequired,
|
||||
baseUrl: PropTypes.string,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import clipboardCopy from 'clipboard-copy'
|
||||
import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url'
|
||||
import { generateMarkup } from '../../lib/generate-image-markup'
|
||||
import { objectOfKeyValuesPropType } from '../../lib/service-definitions/service-definition-prop-types'
|
||||
import { Badge } from '../common'
|
||||
import PathBuilder from './path-builder'
|
||||
import QueryStringBuilder from './query-string-builder'
|
||||
@@ -11,14 +12,11 @@ import CopiedContentIndicator from './copied-content-indicator'
|
||||
|
||||
export default class Customizer extends React.Component {
|
||||
static propTypes = {
|
||||
// This is an item from the `examples` array within the
|
||||
// `serviceDefinition` schema.
|
||||
// https://github.com/badges/shields/blob/master/services/service-definitions.js
|
||||
baseUrl: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
pattern: PropTypes.string.isRequired,
|
||||
exampleNamedParams: PropTypes.object.isRequired,
|
||||
exampleQueryParams: PropTypes.object.isRequired,
|
||||
exampleNamedParams: objectOfKeyValuesPropType,
|
||||
exampleQueryParams: objectOfKeyValuesPropType,
|
||||
initialStyle: PropTypes.string,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import styled, { css } from 'styled-components'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
import humanizeString from 'humanize-string'
|
||||
import { objectOfKeyValuesPropType } from '../../lib/service-definitions/service-definition-prop-types'
|
||||
import { patternToOptions } from '../../lib/pattern-helpers'
|
||||
import { noAutocorrect, StyledInput } from '../common'
|
||||
import {
|
||||
@@ -70,7 +71,7 @@ const NamedParamCaption = styled(BuilderCaption)`
|
||||
export default class PathBuilder extends React.Component {
|
||||
static propTypes = {
|
||||
pattern: PropTypes.string.isRequired,
|
||||
exampleParams: PropTypes.object.isRequired,
|
||||
exampleParams: objectOfKeyValuesPropType,
|
||||
onChange: PropTypes.func,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import styled from 'styled-components'
|
||||
import humanizeString from 'humanize-string'
|
||||
import { stringify as stringifyQueryString } from 'query-string'
|
||||
import { advertisedStyles } from '../../../supported-features.json'
|
||||
import { objectOfKeyValuesPropType } from '../../lib/service-definitions/service-definition-prop-types'
|
||||
import { noAutocorrect, StyledInput } from '../common'
|
||||
import {
|
||||
BuilderContainer,
|
||||
@@ -280,7 +281,7 @@ export default class QueryStringBuilder extends React.Component {
|
||||
}
|
||||
}
|
||||
QueryStringBuilder.propTypes = {
|
||||
exampleParams: PropTypes.object.isRequired,
|
||||
exampleParams: objectOfKeyValuesPropType,
|
||||
initialStyle: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Modal from 'react-modal'
|
||||
import styled from 'styled-components'
|
||||
import { examplePropType } from '../../lib/service-definitions/service-definition-prop-types'
|
||||
import { BaseFont } from '../common'
|
||||
import MarkupModalContent from './markup-modal-content'
|
||||
|
||||
@@ -11,10 +12,7 @@ const ContentContainer = styled(BaseFont)`
|
||||
|
||||
export default class MarkupModal extends React.Component {
|
||||
static propTypes = {
|
||||
// This is an item from the `examples` array within the
|
||||
// `serviceDefinition` schema.
|
||||
// https://github.com/badges/shields/blob/master/services/service-definitions.js
|
||||
example: PropTypes.object,
|
||||
example: examplePropType,
|
||||
baseUrl: PropTypes.string.isRequired,
|
||||
onRequestClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import { examplePropType } from '../../lib/service-definitions/service-definition-prop-types'
|
||||
import { H3 } from '../common'
|
||||
import Customizer from '../customizer/customizer'
|
||||
|
||||
@@ -11,10 +12,7 @@ const Documentation = styled.div`
|
||||
|
||||
export default class MarkupModalContent extends React.Component {
|
||||
static propTypes = {
|
||||
// This is an item from the `examples` array within the
|
||||
// `serviceDefinition` schema.
|
||||
// https://github.com/badges/shields/blob/master/services/service-definitions.js
|
||||
example: PropTypes.object,
|
||||
example: examplePropType,
|
||||
baseUrl: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
// This should be kept in sync with the schema in
|
||||
// `core/base-service/service-definitions.js`.
|
||||
|
||||
const arrayOfStringsPropType = PropTypes.arrayOf(PropTypes.string.isRequired)
|
||||
.isRequired
|
||||
const objectOfKeyValuesPropType = PropTypes.objectOf(PropTypes.string)
|
||||
.isRequired
|
||||
|
||||
const examplePropType = PropTypes.exact({
|
||||
title: PropTypes.string.isRequired,
|
||||
example: PropTypes.exact({
|
||||
pattern: PropTypes.string.isRequired,
|
||||
namedParams: objectOfKeyValuesPropType,
|
||||
queryParams: objectOfKeyValuesPropType,
|
||||
}).isRequired,
|
||||
preview: PropTypes.exact({
|
||||
label: PropTypes.string,
|
||||
message: PropTypes.string.isRequired,
|
||||
color: PropTypes.string.isRequired,
|
||||
style: PropTypes.string,
|
||||
namedLogo: PropTypes.string,
|
||||
}).isRequired,
|
||||
keywords: arrayOfStringsPropType,
|
||||
documentation: PropTypes.exact({
|
||||
__html: PropTypes.string.isRequired,
|
||||
}),
|
||||
})
|
||||
|
||||
const serviceDefinitionPropType = PropTypes.exact({
|
||||
category: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
isDeprecated: PropTypes.bool.isRequired,
|
||||
// Route is missing for e.g. Buildkite.
|
||||
route: PropTypes.oneOfType([
|
||||
PropTypes.exact({
|
||||
pattern: PropTypes.string.isRequired,
|
||||
queryParams: arrayOfStringsPropType,
|
||||
}),
|
||||
PropTypes.exact({
|
||||
format: PropTypes.string.isRequired,
|
||||
queryParams: arrayOfStringsPropType,
|
||||
}),
|
||||
]),
|
||||
examples: PropTypes.arrayOf(examplePropType).isRequired,
|
||||
}).isRequired
|
||||
|
||||
export {
|
||||
arrayOfStringsPropType,
|
||||
objectOfKeyValuesPropType,
|
||||
examplePropType,
|
||||
serviceDefinitionPropType,
|
||||
}
|
||||
Reference in New Issue
Block a user