diff --git a/core/badge-urls/make-badge-url.js b/core/badge-urls/make-badge-url.js index 6646e95ab9787e77ec237ed695fad81dfac8fbd2..3fd23042e6d56e3e187f1e6ea3ae489cae558314 100644 --- a/core/badge-urls/make-badge-url.js +++ b/core/badge-urls/make-badge-url.js @@ -71,9 +71,43 @@ function staticBadgeUrl({ return `${baseUrl}/badge/${path}.${format}${suffix}` } +function dynamicBadgeUrl({ + baseUrl, + datatype, + label, + dataUrl, + query, + prefix, + suffix, + color, + style, + format = 'svg', +}) { + const queryParams = { + label, + url: dataUrl, + query, + style, + } + + if (color) { + queryParams.color = color + } + if (prefix) { + queryParams.prefix = prefix + } + if (suffix) { + queryParams.suffix = suffix + } + + const outQueryString = queryString.stringify(queryParams) + return `${baseUrl}/badge/dynamic/${datatype}.${format}?${outQueryString}` +} + module.exports = { badgeUrlFromPath, badgeUrlFromPattern, encodeField, staticBadgeUrl, + dynamicBadgeUrl, } diff --git a/core/badge-urls/make-badge-url.spec.js b/core/badge-urls/make-badge-url.spec.js index 491035a59aa96a02646813f536ab1018e6f40059..3ca61b9fcccaf73a2ff39eb7a6b763facb23fe6d 100644 --- a/core/badge-urls/make-badge-url.spec.js +++ b/core/badge-urls/make-badge-url.spec.js @@ -6,6 +6,7 @@ const { badgeUrlFromPattern, encodeField, staticBadgeUrl, + dynamicBadgeUrl, } = require('./make-badge-url') describe('Badge URL generation functions', function() { @@ -79,4 +80,50 @@ describe('Badge URL generation functions', function() { color: 'blue', }).expect('/badge/-blue-blue.svg') }) + + test(dynamicBadgeUrl, () => { + const dataUrl = 'http://example.com/foo.json' + const query = '$.bar' + const prefix = 'value: ' + given({ + baseUrl: 'http://img.example.com', + datatype: 'json', + label: 'foo', + dataUrl, + query, + prefix, + style: 'plastic', + }).expect( + [ + 'http://img.example.com/badge/dynamic/json.svg', + '?label=foo', + `&prefix=${encodeURIComponent(prefix)}`, + `&query=${encodeURIComponent(query)}`, + '&style=plastic', + `&url=${encodeURIComponent(dataUrl)}`, + ].join('') + ) + const suffix = '<- value' + const color = 'blue' + given({ + baseUrl: 'http://img.example.com', + datatype: 'json', + label: 'foo', + dataUrl, + query, + suffix, + color, + style: 'plastic', + }).expect( + [ + 'http://img.example.com/badge/dynamic/json.svg', + '?color=blue', + '&label=foo', + `&query=${encodeURIComponent(query)}`, + '&style=plastic', + `&suffix=${encodeURIComponent(suffix)}`, + `&url=${encodeURIComponent(dataUrl)}`, + ].join('') + ) + }) }) diff --git a/frontend/components/customizer/customizer.js b/frontend/components/customizer/customizer.js index 0e1d12debfe25315c30ca49b8b22728f5ca6e4e8..b3e35540f6c9ce20cedae205e1a8ccdd6ac2bfda 100644 --- a/frontend/components/customizer/customizer.js +++ b/frontend/components/customizer/customizer.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import clipboardCopy from 'clipboard-copy' -import { staticBadgeUrl } from '../../lib/badge-url' +import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url' import { generateMarkup } from '../../lib/generate-image-markup' import { Badge } from '../common' import PathBuilder from './path-builder' @@ -61,12 +61,11 @@ export default class Customizer extends React.Component { if (pathIsComplete) { src = this.generateBuiltBadgeUrl() } else { - src = staticBadgeUrl( + src = staticBadgeUrl({ baseUrl, - 'preview', - 'some parameters missing', - 'lightgray' - ) + label: 'preview', + message: 'some parameters missing', + }) } return ( <p> diff --git a/frontend/components/development/logo-page.js b/frontend/components/development/logo-page.js index cf9577a89c109c3fe9d392e608484446a89fc558..0fecbe4bd6ffa27fcafb5e07bf08ebef0e3fdc2d 100644 --- a/frontend/components/development/logo-page.js +++ b/frontend/components/development/logo-page.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import { staticBadgeUrl } from '../../lib/badge-url' +import { staticBadgeUrl } from '../../../core/badge-urls/make-badge-url' import { baseUrl } from '../../constants' import Meta from '../meta' import Header from '../header' @@ -33,16 +33,24 @@ const NamedLogoTable = ({ logoNames }) => ( <td> <Badge alt={`logo: ${name}`} - src={staticBadgeUrl(baseUrl, 'named logo', name, 'blue', { - logo: name, + src={staticBadgeUrl({ + baseUrl, + label: 'named logo', + message: name, + color: 'blue', + namedLogo: name, })} /> </td> <td> <Badge alt={`logo: ${name}`} - src={staticBadgeUrl(baseUrl, 'Named Logo', name, 'blue', { - logo: name, + src={staticBadgeUrl({ + baseUrl, + label: 'Named Logo', + message: name, + color: 'blue', + namedLogo: name, style: 'social', })} /> diff --git a/frontend/components/dynamic-badge-maker.js b/frontend/components/dynamic-badge-maker.js index 437dd9a5b1bb103e99605f7f7ff92ffdd29f397c..0eefa166c1dbc5ef4479cb01aaf000130f5e9203 100644 --- a/frontend/components/dynamic-badge-maker.js +++ b/frontend/components/dynamic-badge-maker.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { dynamicBadgeUrl } from '../lib/badge-url' +import { dynamicBadgeUrl } from '../../core/badge-urls/make-badge-url' import { InlineInput } from './common' export default class DynamicBadgeMaker extends React.Component { @@ -11,7 +11,7 @@ export default class DynamicBadgeMaker extends React.Component { state = { datatype: '', label: '', - url: '', + dataUrl: '', query: '', color: '', prefix: '', @@ -19,9 +19,22 @@ export default class DynamicBadgeMaker extends React.Component { } makeBadgeUrl() { - const { datatype, label, url, query, color, prefix, suffix } = this.state + const { + datatype, + label, + dataUrl, + query, + color, + prefix, + suffix, + } = this.state const { baseUrl: baseUrl = document.location.href } = this.props - return dynamicBadgeUrl(baseUrl, datatype, label, url, query, { + return dynamicBadgeUrl({ + baseUrl: baseUrl || window.location.href, + datatype, + label, + dataUrl, + query, color, prefix, suffix, @@ -34,8 +47,8 @@ export default class DynamicBadgeMaker extends React.Component { } get isValid() { - const { datatype, label, url, query } = this.state - return datatype && label && url && query + const { datatype, label, dataUrl, query } = this.state + return datatype && label && dataUrl && query } render() { @@ -61,9 +74,9 @@ export default class DynamicBadgeMaker extends React.Component { /> <InlineInput className="short" - onChange={event => this.setState({ url: event.target.value })} - placeholder="url" - value={this.state.url} + onChange={event => this.setState({ dataUrl: event.target.value })} + placeholder="data url" + value={this.state.dataUrl} /> <InlineInput className="short" diff --git a/frontend/components/footer.js b/frontend/components/footer.js index 5a69f5b1f826bc8e8c4dd3bbceb300af9de96f54..a95d870ae5661390fc4e5b70cc20a706e98cd88e 100644 --- a/frontend/components/footer.js +++ b/frontend/components/footer.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import resolveUrl from '../lib/resolve-url' +import { badgeUrlFromPath } from '../../core/badge-urls/make-badge-url' import { H2 } from './common' const SpacedA = styled.a` @@ -16,42 +16,55 @@ const Footer = ({ baseUrl }) => ( <p> <object alt="Follow @shields_io" - data={resolveUrl( - '/twitter/follow/shields_io.svg?style=social&label=Follow', - baseUrl - )} + data={badgeUrlFromPath({ + baseUrl, + path: '/twitter/follow/shields_io', + queryParams: { label: 'Follow' }, + style: 'social', + })} />{' '} {} <object alt="Donate to us!" - data={resolveUrl( - '/opencollective/backers/shields.svg?style=social&link=https://opencollective.com/shields', - baseUrl - )} + data={badgeUrlFromPath({ + baseUrl, + path: '/opencollective/backers/shields', + queryParams: { link: 'https://opencollective.com/shields' }, + style: 'social', + })} />{' '} {} <object alt="Donate to us!" - data={resolveUrl( - '/opencollective/sponsors/shields.svg?style=social&link=https://opencollective.com/shields', - baseUrl - )} + data={badgeUrlFromPath({ + baseUrl, + path: '/opencollective/sponsors/shields', + queryParams: { link: 'https://opencollective.com/shields' }, + style: 'social', + })} />{' '} {} <object alt="Fork on GitHub" - data={resolveUrl( - '/github/forks/badges/shields.svg?style=social&label=Fork', - baseUrl - )} + data={badgeUrlFromPath({ + baseUrl, + path: '/github/forks/badges/shields', + queryParams: { label: 'Fork' }, + style: 'social', + })} />{' '} {} <object alt="chat on Discord" - data={resolveUrl( - '/discord/308323056592486420.svg?style=social&label=Chat&link=https://discord.gg/HjJCwm5', - baseUrl - )} + data={badgeUrlFromPath({ + baseUrl, + path: '/discord/308323056592486420', + queryParams: { + label: 'Chat', + link: 'link=https://discord.gg/HjJCwm5', + }, + style: 'social', + })} /> </p> diff --git a/frontend/components/static-badge-maker.js b/frontend/components/static-badge-maker.js index 10db22f78d34dc6c7806b60c7196590fba58c652..8a9b8003a051615274bbb6b34a29043467c2835b 100644 --- a/frontend/components/static-badge-maker.js +++ b/frontend/components/static-badge-maker.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { staticBadgeUrl } from '../lib/badge-url' +import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url' import { InlineInput } from './common' export default class StaticBadgeMaker extends React.Component { @@ -9,8 +9,8 @@ export default class StaticBadgeMaker extends React.Component { } state = { - subject: '', - status: '', + label: '', + message: '', color: '', } @@ -18,13 +18,13 @@ export default class StaticBadgeMaker extends React.Component { e.preventDefault() const { baseUrl } = this.props - const { subject, status, color } = this.state - const badgeUrl = staticBadgeUrl( - baseUrl || window.location.href, - subject, - status, - color - ) + const { label, message, color } = this.state + const badgeUrl = staticBadgeUrl({ + baseUrl: baseUrl || window.location.href, + label, + message, + color, + }) document.location = badgeUrl } @@ -33,14 +33,14 @@ export default class StaticBadgeMaker extends React.Component { return ( <form onSubmit={e => this.handleSubmit(e)}> <InlineInput - onChange={event => this.setState({ subject: event.target.value })} - placeholder="subject" - value={this.state.subject} + onChange={event => this.setState({ label: event.target.value })} + placeholder="label" + value={this.state.label} /> <InlineInput - onChange={event => this.setState({ status: event.target.value })} - placeholder="status" - value={this.state.status} + onChange={event => this.setState({ message: event.target.value })} + placeholder="message" + value={this.state.message} /> <InlineInput list="default-colors" diff --git a/frontend/components/suggestion-and-search.js b/frontend/components/suggestion-and-search.js index 82a631f79d2c4ed262dd96b4740ce7e4a3be9807..dc98159c384ffd5b3edf8f2afd58b502b352bfa9 100644 --- a/frontend/components/suggestion-and-search.js +++ b/frontend/components/suggestion-and-search.js @@ -2,7 +2,6 @@ import React from 'react' import PropTypes from 'prop-types' import fetchPonyfill from 'fetch-ponyfill' import debounce from 'lodash.debounce' -import resolveUrl from '../lib/resolve-url' import BadgeExamples from './badge-examples' import { BlockInput } from './common' @@ -42,7 +41,7 @@ export default class SuggestionAndSearch extends React.Component { const { baseUrl } = this.props const { projectUrl } = this.state - const url = resolveUrl('/$suggest/v1', baseUrl, { url: projectUrl }) + const url = `${baseUrl}/$suggest/v1?url=${encodeURIComponent(projectUrl)}` const fetch = window.fetch || fetchPonyfill const res = await fetch(url) diff --git a/frontend/components/usage.js b/frontend/components/usage.js index 4c54ea1d1745a65f6a15150ebb6a05c5a9044432..f323bfa2374adc27602386003c733c35a05e2be9 100644 --- a/frontend/components/usage.js +++ b/frontend/components/usage.js @@ -2,7 +2,7 @@ import React from 'react' import { Link } from 'gatsby' import PropTypes from 'prop-types' import styled from 'styled-components' -import { staticBadgeUrl } from '../lib/badge-url' +import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url' import { advertisedStyles, shieldsLogos } from '../../supported-features.json' import StaticBadgeMaker from './static-badge-maker' import DynamicBadgeMaker from './dynamic-badge-maker' @@ -69,7 +69,7 @@ const ColorExamples = ({ baseUrl, colors }) => ( <Badge alt={color} key={color} - src={staticBadgeUrl(baseUrl, '', color, color)} + src={staticBadgeUrl({ baseUrl, label: '', message: color, color })} /> ))} </span> @@ -91,8 +91,12 @@ export default class Usage extends React.PureComponent { <tbody> {advertisedStyles.map(style => { const snippet = `?style=${style}&logo=appveyor` - const badgeUrl = staticBadgeUrl(baseUrl, 'style', style, 'green', { - logo: 'appveyor', + const badgeUrl = staticBadgeUrl({ + baseUrl, + label: 'style', + message: style, + color: 'green', + namedLogo: 'appveyor', style, }) return ( diff --git a/frontend/lib/badge-url.js b/frontend/lib/badge-url.js deleted file mode 100644 index 5ff3aca4c72724823e28806d551eaf2c87e89974..0000000000000000000000000000000000000000 --- a/frontend/lib/badge-url.js +++ /dev/null @@ -1,53 +0,0 @@ -import resolveUrl from './resolve-url' -import { staticBadgeUrl as makeStaticBadgeUrl } from '../../core/badge-urls/make-badge-url' - -export default function resolveBadgeUrl( - url, - baseUrl, - { longCache, style, queryParams: inQueryParams, format = 'svg' } = {} -) { - const outQueryParams = Object.assign({}, inQueryParams) - if (longCache) { - outQueryParams.maxAge = '2592000' - } - if (style) { - outQueryParams.style = style - } - - return resolveUrl(`${url}.${format}`, baseUrl, outQueryParams) -} - -export function staticBadgeUrl(baseUrl, label, message, color, options) { - const path = makeStaticBadgeUrl({ label, message, color }) - return resolveUrl(path, baseUrl, options) -} - -// Options can include: { prefix, suffix, color, longCache, style, queryParams } -export function dynamicBadgeUrl( - baseUrl, - datatype, - label, - dataUrl, - query, - { prefix, suffix, color, queryParams = {}, ...rest } = {} -) { - Object.assign(queryParams, { - label, - url: dataUrl, - query, - }) - - if (color) { - queryParams.color = color - } - if (prefix) { - queryParams.prefix = prefix - } - if (suffix) { - queryParams.suffix = suffix - } - - const outOptions = Object.assign({ queryParams }, rest) - - return resolveBadgeUrl(`/badge/dynamic/${datatype}`, baseUrl, outOptions) -} diff --git a/frontend/lib/badge-url.spec.js b/frontend/lib/badge-url.spec.js deleted file mode 100644 index 4817e4cb133c00516bb528b6b613759ceec679b1..0000000000000000000000000000000000000000 --- a/frontend/lib/badge-url.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { test, given } from 'sazerac' -import resolveBadgeUrl, { staticBadgeUrl, dynamicBadgeUrl } from './badge-url' - -const resolveBadgeUrlWithLongCache = (url, baseUrl) => - resolveBadgeUrl(url, baseUrl, { longCache: true }) - -describe('Badge URL functions', function() { - test(resolveBadgeUrl, () => { - given('/badge/foo-bar-blue', undefined).expect('/badge/foo-bar-blue.svg') - given('/badge/foo-bar-blue', 'http://example.com').expect( - 'http://example.com/badge/foo-bar-blue.svg' - ) - }) - - test(resolveBadgeUrlWithLongCache, () => { - given('/badge/foo-bar-blue', undefined).expect( - '/badge/foo-bar-blue.svg?maxAge=2592000' - ) - given('/badge/foo-bar-blue', 'http://example.com').expect( - 'http://example.com/badge/foo-bar-blue.svg?maxAge=2592000' - ) - }) - - test(staticBadgeUrl, () => { - given('http://img.example.com', 'foo', 'bar', 'blue', { - style: 'plastic', - }).expect('http://img.example.com/badge/foo-bar-blue.svg?style=plastic') - }) - - test(dynamicBadgeUrl, () => { - const dataUrl = 'http://example.com/foo.json' - const query = '$.bar' - const prefix = 'value: ' - - given('http://img.example.com', 'json', 'foo', dataUrl, query, { - prefix, - style: 'plastic', - }).expect( - [ - 'http://img.example.com/badge/dynamic/json.svg', - '?label=foo', - `&url=${encodeURIComponent(dataUrl)}`, - `&query=${encodeURIComponent(query)}`, - `&prefix=${encodeURIComponent(prefix)}`, - '&style=plastic', - ].join('') - ) - - const suffix = '<- value' - const color = 'blue' - given('http://img.example.com', 'json', 'foo', dataUrl, query, { - suffix, - color, - style: 'plastic', - }).expect( - [ - 'http://img.example.com/badge/dynamic/json.svg', - '?label=foo', - `&url=${encodeURIComponent(dataUrl)}`, - `&query=${encodeURIComponent(query)}`, - '&color=blue', - `&suffix=${encodeURIComponent(suffix)}`, - '&style=plastic', - ].join('') - ) - }) -}) diff --git a/frontend/lib/resolve-url.js b/frontend/lib/resolve-url.js deleted file mode 100644 index f0f626ccbae2cbf12756a4e62fcafc372afbfa1f..0000000000000000000000000000000000000000 --- a/frontend/lib/resolve-url.js +++ /dev/null @@ -1,17 +0,0 @@ -// I played with build-url and url-resolve-browser and neither of them did the -// right thing. Previously this was based on url-path, which patched around -// the URL API. This caused problems in Firefox 57, but only in the production -// build. - -// Let's rewrite these without a deprecated API! -// eslint-disable-next-line node/no-deprecated-api -import { resolve, parse, format } from 'url' - -// baseUrl and queryParams are optional. -export default function resolveUrl(url, baseUrl, queryParams) { - const resolved = baseUrl ? resolve(baseUrl, url) : url - const parsed = parse(resolved, /* parseQueryString */ true) - parsed.query = Object.assign({}, parsed.query, queryParams) - delete parsed.search - return format(parsed) -} diff --git a/frontend/lib/resolve-url.spec.js b/frontend/lib/resolve-url.spec.js deleted file mode 100644 index 25c5ffb9af3f8a1d24c4f6444217e57b2eb5f47e..0000000000000000000000000000000000000000 --- a/frontend/lib/resolve-url.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -import { test, given, forCases } from 'sazerac' -import resolveUrl from './resolve-url' - -describe('URL resolver', function() { - test(resolveUrl, () => { - forCases([ - given('/foo/bar'), - given('/foo/bar', '/'), - given('/foo/bar', '/baz'), - given('/foo/bar', '/baz/'), - given('/foo/bar', ''), - given('/foo/bar', undefined), - ]).expect('/foo/bar') - - given('foo/bar', '/baz/').expect('/baz/foo/bar') - - forCases([ - given('http://foo/bar'), - given('bar', 'http://foo/'), - given('/bar', 'http://foo/'), - ]).expect('http://foo/bar') - - given('/foo/bar', '/baz', { baz: 'bazinga' }).expect('/foo/bar?baz=bazinga') - - given('/foo/bar?thing=1', undefined, { other: '2' }).expect( - '/foo/bar?thing=1&other=2' - ) - }) -}) diff --git a/frontend/pages/endpoint.js b/frontend/pages/endpoint.js index f7a7f3f5e5e963b42aec5587c3c51dd31f0d2c71..5f35314c74463e272bee94df4a58c102e74d9bbb 100644 --- a/frontend/pages/endpoint.js +++ b/frontend/pages/endpoint.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import { staticBadgeUrl } from '../lib/badge-url' +import { staticBadgeUrl } from '../../core/badge-urls/make-badge-url' import { baseUrl } from '../constants' import Meta from '../components/meta' import Header from '../components/header' @@ -101,7 +101,12 @@ const EndpointPage = () => ( <p>Shields response:</p> <Badge alt="hello | sweet world" - src={staticBadgeUrl(baseUrl, 'hello', 'sweet world', 'orange')} + src={staticBadgeUrl({ + baseUrl, + label: 'hello', + message: 'sweet world', + color: 'orange', + })} /> <Explanation> <p>