diff --git a/core/base-service/service-definitions.js b/core/base-service/service-definitions.js index 597414a3aa..b38b04efae 100644 --- a/core/base-service/service-definitions.js +++ b/core/base-service/service-definitions.js @@ -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([]) diff --git a/frontend/components/badge-examples.js b/frontend/components/badge-examples.js index 929a7f1f24..d6e9efd353 100644 --- a/frontend/components/badge-examples.js +++ b/frontend/components/badge-examples.js @@ -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, } diff --git a/frontend/components/customizer/customizer.js b/frontend/components/customizer/customizer.js index b3e35540f6..dd533d5e41 100644 --- a/frontend/components/customizer/customizer.js +++ b/frontend/components/customizer/customizer.js @@ -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, } diff --git a/frontend/components/customizer/path-builder.js b/frontend/components/customizer/path-builder.js index 878dc4fd63..a27011a99c 100644 --- a/frontend/components/customizer/path-builder.js +++ b/frontend/components/customizer/path-builder.js @@ -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, } diff --git a/frontend/components/customizer/query-string-builder.js b/frontend/components/customizer/query-string-builder.js index 15583c22d9..d27e925da4 100644 --- a/frontend/components/customizer/query-string-builder.js +++ b/frontend/components/customizer/query-string-builder.js @@ -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, } diff --git a/frontend/components/markup-modal/index.js b/frontend/components/markup-modal/index.js index 246d5e555e..1375cebeee 100644 --- a/frontend/components/markup-modal/index.js +++ b/frontend/components/markup-modal/index.js @@ -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, } diff --git a/frontend/components/markup-modal/markup-modal-content.js b/frontend/components/markup-modal/markup-modal-content.js index 89d4ff7423..c6b5265009 100644 --- a/frontend/components/markup-modal/markup-modal-content.js +++ b/frontend/components/markup-modal/markup-modal-content.js @@ -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, } diff --git a/frontend/lib/service-definitions/service-definition-prop-types.js b/frontend/lib/service-definitions/service-definition-prop-types.js new file mode 100644 index 0000000000..02621beeb8 --- /dev/null +++ b/frontend/lib/service-definitions/service-definition-prop-types.js @@ -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, +}