Add frontend prop-type validation for the service definition schema (#3063)

Closes #2702
This commit is contained in:
Paul Melnikow
2019-02-23 15:34:49 -05:00
committed by GitHub
parent 7d1d74cfcf
commit 09e902fa38
8 changed files with 70 additions and 16 deletions

View File

@@ -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([])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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