Compare commits
1 Commits
server-202
...
missing-sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d60bca325f |
4
.github/ISSUE_TEMPLATE/3_Badge_request.yml
vendored
4
.github/ISSUE_TEMPLATE/3_Badge_request.yml
vendored
@@ -12,7 +12,7 @@ body:
|
||||
**fetch and display data from an upstream service**.
|
||||
If your suggestion is for a static badge
|
||||
(which shows the same information every time it is requested), it is
|
||||
[already possible to make these](https://shields.io/docs/static-badges).
|
||||
[already possible to make these](https://github.com/badges/shields/blob/master/doc/static-badges.md).
|
||||
We don't add specific routes for badges which only show static information.
|
||||
|
||||
- type: textarea
|
||||
@@ -25,7 +25,7 @@ body:
|
||||
- Which service is this badge for e.g: GitHub, Travis CI
|
||||
- What sort of information should this badge show?
|
||||
Provide an example in plain text e.g: "version | v1.01" or as a static badge
|
||||
(static badge generator can be found at https://shields.io/badges/static-badge )
|
||||
(static badge generator can be found at https://shields.io/#your-badge )
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
9
.github/actions/close-bot/helpers.js
vendored
9
.github/actions/close-bot/helpers.js
vendored
@@ -28,7 +28,7 @@ function allChangelogLinesAreVersionBump(changelogLines) {
|
||||
changelogLines.length > 0 &&
|
||||
changelogLines.length ===
|
||||
changelogLines.filter(line =>
|
||||
line.includes('Version bump only for package'),
|
||||
line.includes('Version bump only for package')
|
||||
).length
|
||||
)
|
||||
}
|
||||
@@ -52,15 +52,14 @@ function isPointlessVersionBump(body) {
|
||||
.filter(line => !line.startsWith('<h'))
|
||||
.filter(line => !line.startsWith('<p>All notable changes'))
|
||||
.filter(
|
||||
line =>
|
||||
!line.startsWith('See <a href="https://conventionalcommits.org">'),
|
||||
line => !line.startsWith('See <a href="https://conventionalcommits.org">')
|
||||
)
|
||||
.filter(line => !line.startsWith('<!--'))
|
||||
.filter(
|
||||
line =>
|
||||
!line.startsWith(
|
||||
'<p><a href="https://www.gatsbyjs.com/docs/reference/release-notes/',
|
||||
),
|
||||
'<p><a href="https://www.gatsbyjs.com/docs/reference/release-notes/'
|
||||
)
|
||||
)
|
||||
return allChangelogLinesAreVersionBump(changelogLines)
|
||||
}
|
||||
|
||||
2
.github/actions/draft-release/Dockerfile
vendored
2
.github/actions/draft-release/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM node:18-bullseye
|
||||
FROM node:12-buster
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y jq
|
||||
|
||||
9
.github/actions/draft-release/entrypoint.sh
vendored
9
.github/actions/draft-release/entrypoint.sh
vendored
@@ -2,17 +2,14 @@
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
# mark workspace dir as 'safe'
|
||||
git config --system --add safe.directory '/github/workspace'
|
||||
# Set up a git user
|
||||
git config user.name "release[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
|
||||
# Find last server-YYYY-MM-DD tag
|
||||
git fetch --unshallow --tags
|
||||
LAST_TAG=$(git tag | grep server | tail -n 1)
|
||||
|
||||
# Set up a git user
|
||||
git config user.name "release[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
|
||||
# Find the marker in CHANGELOG.md
|
||||
INSERT_POINT=$(grep -n "^\-\-\-$" CHANGELOG.md | cut -f1 -d:)
|
||||
INSERT_POINT=$((INSERT_POINT+1))
|
||||
|
||||
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@@ -19,10 +19,6 @@ runs:
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
- name: Install NPM 9
|
||||
run: npm install -g npm@^9.0.0
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.cypress == 'false' }}
|
||||
env:
|
||||
|
||||
5
.github/scripts/deploy-review-app.sh
vendored
5
.github/scripts/deploy-review-app.sh
vendored
@@ -10,11 +10,12 @@ org="shields-io"
|
||||
# This will fail if $PR_NUMBER is not a valid PR
|
||||
pr_json=$(curl --fail "https://api.github.com/repos/badges/shields/pulls/$PR_NUMBER")
|
||||
|
||||
# Checkout the PR branch
|
||||
# Attempt to apply the PR diff to the target branch
|
||||
# This will fail if it does not merge cleanly
|
||||
git config user.name "actions[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
git fetch origin "pull/$PR_NUMBER/head:pr-$PR_NUMBER"
|
||||
git checkout "pr-$PR_NUMBER"
|
||||
git merge "pr-$PR_NUMBER"
|
||||
|
||||
# If the app does not already exist, create it
|
||||
if ! flyctl status --app "$app"; then
|
||||
|
||||
3
.github/workflows/build-docker-image.yml
vendored
3
.github/workflows/build-docker-image.yml
vendored
@@ -1,9 +1,6 @@
|
||||
name: Build Docker Image
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
build-docker-image:
|
||||
|
||||
67
.github/workflows/test-bug-run-badge.yml
vendored
67
.github/workflows/test-bug-run-badge.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Test new bug report badge
|
||||
run-name: Test bug report on issue ${{ github.event.issue.number }}
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
extract-bug-badge-url:
|
||||
if: ${{ contains(github.event.issue.labels.*.name, 'question') }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runBadgeTest: ${{ steps.testCondition.outputs.runNext }}
|
||||
link: ${{ steps.testCondition.outputs.link }}
|
||||
steps:
|
||||
- name: Test badge test run conditions
|
||||
id: testCondition
|
||||
run: |
|
||||
product=$(echo "${{ github.event.issue.body }}" | grep -A2 "Are you experiencing an issue with.*" | tail -n 1)
|
||||
link=$(echo "${{ github.event.issue.body }}" | grep -A2 "Link to the badge.*" | tail -n 1)
|
||||
|
||||
if [[ "$product" == "shields.io" && "$link" == "https://img.shields.io"* ]]; then
|
||||
echo "runNext=true" >> "$GITHUB_OUTPUT"
|
||||
echo "link=$link" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Conditions not met. Skipping the workflow..."
|
||||
echo "runNext=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
run-bug-badge-url-test:
|
||||
needs: extract-bug-badge-url
|
||||
if: needs.extract-bug-badge-url.outputs.runBadgeTest == 'true'
|
||||
permissions:
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 16
|
||||
cypress: false
|
||||
|
||||
- name: Output debug info
|
||||
env:
|
||||
TEST_BADGE_LINK: '${{ needs.extract-bug-badge-url.outputs.link }}'
|
||||
run: npm run badge $TEST_BADGE_LINK
|
||||
|
||||
- name: Add Comment to Issue
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const issueNumber = context.issue.number;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const runId = context.runId;
|
||||
const jobUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`;
|
||||
const issueComment = `
|
||||
Badge tested using \`npm run badge ${{ needs.extract-bug-badge-url.outputs.link }}\`
|
||||
Output is available [here](${jobUrl})
|
||||
`;
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
body: issueComment
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Integration@node 18
|
||||
name: Integration@node 17
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-integration-18:
|
||||
test-integration-17:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PAT_EXISTS: ${{ secrets.GH_PAT != '' }}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 17
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Main@node 18
|
||||
name: Main@node 17
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-main-18:
|
||||
test-main-17:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 17
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
8
.github/workflows/test-package-cli.yml
vendored
8
.github/workflows/test-package-cli.yml
vendored
@@ -16,9 +16,12 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- node: '14'
|
||||
engine-strict: 'false'
|
||||
- node: '16'
|
||||
engine-strict: 'false'
|
||||
- node: '18'
|
||||
- node: '20'
|
||||
engine-strict: 'true'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -29,6 +32,9 @@ jobs:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
NPM_CONFIG_ENGINE_STRICT: ${{ matrix.engine-strict }}
|
||||
run: |
|
||||
cd badge-maker
|
||||
npm install
|
||||
|
||||
4
.github/workflows/test-package-lib.yml
vendored
4
.github/workflows/test-package-lib.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- node: '14'
|
||||
engine-strict: 'false'
|
||||
- node: '16'
|
||||
engine-strict: 'true'
|
||||
- node: '18'
|
||||
engine-strict: 'false'
|
||||
- node: '20'
|
||||
engine-strict: 'false'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
name: Services@node 18
|
||||
name: Services@node 17
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
test-services-18:
|
||||
test-services-17:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -17,7 +14,7 @@ jobs:
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 17
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
3
.github/workflows/test-services.yml
vendored
3
.github/workflows/test-services.yml
vendored
@@ -2,9 +2,6 @@ name: Services
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
test-services:
|
||||
|
||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -4,51 +4,6 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
|
||||
|
||||
---
|
||||
|
||||
## server-2023-08-01
|
||||
|
||||
- Convert `examples` arrays to `openApi` objects (part 1) [#9320](https://github.com/badges/shields/issues/9320)
|
||||
- Migrate from docs.rs' builds API to status API [#9422](https://github.com/badges/shields/issues/9422)
|
||||
- [OpenVSX] Fix OpenVSX API call for unversioned package URLs [#9408](https://github.com/badges/shields/issues/9408)
|
||||
- Add support for [Lemmy] [#9368](https://github.com/badges/shields/issues/9368)
|
||||
- upgrade to npm 9 [#9323](https://github.com/badges/shields/issues/9323)
|
||||
- Go back to default YouTube cache [#9372](https://github.com/badges/shields/issues/9372)
|
||||
- Add [GitHubDiscussionsSearch] and GitHubRepoDiscussionsSearch service [#9340](https://github.com/badges/shields/issues/9340)
|
||||
- Allow user to filter github tags and releases [#9193](https://github.com/badges/shields/issues/9193)
|
||||
- don't URL encode slash in [githubactionsworkflow] badge [#9322](https://github.com/badges/shields/issues/9322)
|
||||
- add a bit of border to select boxes [#9348](https://github.com/badges/shields/issues/9348)
|
||||
- deprecate [snyk] badges [#9349](https://github.com/badges/shields/issues/9349)
|
||||
- increase max-age on [docker] badges, again [#9350](https://github.com/badges/shields/issues/9350) [#9369](https://github.com/badges/shields/issues/9369)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-07-02
|
||||
|
||||
By far the most significant change in this release is the long-awaited launch of the re-designed frontend:
|
||||
|
||||
- migrate frontend to docusaurus [#9014](https://github.com/badges/shields/issues/9014)
|
||||
- fix a load of spacing issues in frontend content [#9281](https://github.com/badges/shields/issues/9281)
|
||||
- set a sensible meta description [#9283](https://github.com/badges/shields/issues/9283)
|
||||
- chore(frontend): open homepage feature links in new tab [#9300](https://github.com/badges/shields/issues/9300)
|
||||
- adapt opencollective images to theme background [#9298](https://github.com/badges/shields/issues/9298)
|
||||
- temp fix: wrap code examples tabs in narrow browser windows [#9302](https://github.com/badges/shields/issues/9302)
|
||||
- add a bit of border to text boxes [#9324](https://github.com/badges/shields/issues/9324)
|
||||
|
||||
Other changes in this release:
|
||||
|
||||
- cache [dockerpulls] badges for an hour [#9343](https://github.com/badges/shields/issues/9343)
|
||||
- Mention YouTube API services and link to Google Privacy Policy [#9339](https://github.com/badges/shields/issues/9339)
|
||||
- allow negative timestamps in relative [date] badge [#9321](https://github.com/badges/shields/issues/9321)
|
||||
- upgrade to graphql 16 [#9290](https://github.com/badges/shields/issues/9290)
|
||||
- remove obsolete travis .org examples [#9284](https://github.com/badges/shields/issues/9284)
|
||||
- increase max age on reddit badges [#9282](https://github.com/badges/shields/issues/9282)
|
||||
- feat: Add author filter option for [GithubCommitActivity] [#9251](https://github.com/badges/shields/issues/9251)
|
||||
- Fix: [GithubCommitActivity] invalid branch error handling [#9258](https://github.com/badges/shields/issues/9258)
|
||||
- Implement a pattern for dealing with upstream APIs which are slow on the first hit; affects [endpoint] [#9233](https://github.com/badges/shields/issues/9233)
|
||||
- Delete old deprecated services [#9254](https://github.com/badges/shields/issues/9254)
|
||||
- feat: add 'canceled' status to netlify deploy badge [#9240](https://github.com/badges/shields/issues/9240)
|
||||
- increase default cache on youtube badges [#9238](https://github.com/badges/shields/issues/9238)
|
||||
- embiggen youtube cache, again [#9250](https://github.com/badges/shields/issues/9250)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-06-01
|
||||
|
||||
- feat: Add total commits to [GitHubCommitActivity] [#9196](https://github.com/badges/shields/issues/9196)
|
||||
|
||||
@@ -9,7 +9,7 @@ COPY package.json package-lock.json /usr/src/app/
|
||||
COPY badge-maker /usr/src/app/badge-maker/
|
||||
|
||||
RUN apk add python3 make g++
|
||||
RUN npm install -g "npm@^9.0.0"
|
||||
RUN npm install -g "npm@>=8"
|
||||
# We need dev deps to build the front end. We don't need Cypress, though.
|
||||
RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ This repo hosts:
|
||||
[Make your own badges!][custom badges]
|
||||
(Quick example: `https://img.shields.io/badge/left-right-f39f37`)
|
||||
|
||||
[custom badges]: http://localhost:3000/badges/static-badge
|
||||
[custom badges]: https://shields.io/#your-badge
|
||||
|
||||
### Quickstart
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 4.0.0 [WIP]
|
||||
|
||||
- Drop compatibility with Node < 16
|
||||
- Drop compatibility with Node < 14
|
||||
|
||||
## 3.3.1
|
||||
|
||||
@@ -275,7 +275,7 @@ badge.loadFont('/path/to/Verdana.ttf', err => {
|
||||
{ text: ['build', 'passed'], colorscheme: 'green', template: 'flat' },
|
||||
(svg, err) => {
|
||||
// svg is a string containing your badge
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -7,19 +7,19 @@ expectError(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
style: 'invalid style',
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
@@ -28,7 +28,7 @@ expectType<string>(
|
||||
labelColor: 'green',
|
||||
color: 'red',
|
||||
style: 'flat',
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
const error = new ValidationError()
|
||||
|
||||
@@ -69,7 +69,7 @@ function getLogoElement({ logo, horizPadding, badgeHeight, logoWidth }) {
|
||||
|
||||
function renderBadge(
|
||||
{ links, leftWidth, rightWidth, height, accessibleText },
|
||||
content,
|
||||
content
|
||||
) {
|
||||
const width = leftWidth + rightWidth
|
||||
const leftLink = links[0]
|
||||
@@ -397,7 +397,7 @@ class Plastic extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement],
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -446,7 +446,7 @@ class Flat extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement],
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -478,7 +478,7 @@ class FlatSquare extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[backgroundGroup, this.foregroundGroupElement],
|
||||
[backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -748,7 +748,7 @@ function social({
|
||||
accessibleText,
|
||||
height: externalHeight,
|
||||
},
|
||||
[style, gradients, backgroundGroup, logoElement, foregroundGroup],
|
||||
[style, gradients, backgroundGroup, logoElement, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -978,7 +978,7 @@ function forTheBadge({
|
||||
accessibleText: createAccessibleText({ label, message }),
|
||||
height: BADGE_HEIGHT,
|
||||
},
|
||||
[backgroundGroup, foregroundGroup],
|
||||
[backgroundGroup, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ test(toSvgColor, () => {
|
||||
given('papayawhip').expect('papayawhip')
|
||||
given('purple').expect('purple')
|
||||
forCases([given(''), given(undefined), given('not-a-color')]).expect(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
given('lightgray').expect('#9f9f9f')
|
||||
given('informational').expect('#007ec6')
|
||||
|
||||
@@ -32,7 +32,7 @@ function _validate(format) {
|
||||
]
|
||||
if ('style' in format && !styleValues.includes(format.style)) {
|
||||
throw new ValidationError(
|
||||
`Field \`style\` must be one of (${styleValues.toString()})`,
|
||||
`Field \`style\` must be one of (${styleValues.toString()})`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ function _clean(format) {
|
||||
cleaned[key] = format[key]
|
||||
} else {
|
||||
throw new ValidationError(
|
||||
`Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`,
|
||||
`Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -10,12 +10,12 @@ describe('makeBadge function', function () {
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
expect(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
expect(
|
||||
makeBadge({
|
||||
@@ -23,7 +23,7 @@ describe('makeBadge function', function () {
|
||||
message: 'passed',
|
||||
color: 'green',
|
||||
style: 'flat',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
})
|
||||
|
||||
@@ -32,44 +32,44 @@ describe('makeBadge function', function () {
|
||||
console.log(x)
|
||||
expect(() => makeBadge(x)).to.throw(
|
||||
ValidationError,
|
||||
'makeBadge takes an argument of type object',
|
||||
'makeBadge takes an argument of type object'
|
||||
)
|
||||
})
|
||||
expect(() => makeBadge({})).to.throw(
|
||||
ValidationError,
|
||||
'Field `message` is required',
|
||||
'Field `message` is required'
|
||||
)
|
||||
expect(() => makeBadge({ label: 'build' })).to.throw(
|
||||
ValidationError,
|
||||
'Field `message` is required',
|
||||
'Field `message` is required'
|
||||
)
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', labelColor: 7 }),
|
||||
makeBadge({ label: 'build', message: 'passed', labelColor: 7 })
|
||||
).to.throw(ValidationError, 'Field `labelColor` must be of type string')
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', format: 'png' }),
|
||||
makeBadge({ label: 'build', message: 'passed', format: 'png' })
|
||||
).to.throw(ValidationError, "Unexpected field 'format'")
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', template: 'flat' }),
|
||||
makeBadge({ label: 'build', message: 'passed', template: 'flat' })
|
||||
).to.throw(ValidationError, "Unexpected field 'template'")
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', foo: 'bar' }),
|
||||
makeBadge({ label: 'build', message: 'passed', foo: 'bar' })
|
||||
).to.throw(ValidationError, "Unexpected field 'foo'")
|
||||
expect(() =>
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
style: 'something else',
|
||||
}),
|
||||
})
|
||||
).to.throw(
|
||||
ValidationError,
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)',
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
|
||||
)
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', style: 'popout' }),
|
||||
makeBadge({ label: 'build', message: 'passed', style: 'popout' })
|
||||
).to.throw(
|
||||
ValidationError,
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)',
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,6 +58,6 @@ module.exports = function makeBadge({
|
||||
logoPadding: logo && label.length ? 3 : 0,
|
||||
color: toSvgColor(color),
|
||||
labelColor: toSvgColor(labelColor),
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ const snapshot = require('snap-shot-it')
|
||||
const prettier = require('prettier')
|
||||
const makeBadge = require('./make-badge')
|
||||
|
||||
async function expectBadgeToMatchSnapshot(format) {
|
||||
snapshot(await prettier.format(makeBadge(format), { parser: 'html' }))
|
||||
function expectBadgeToMatchSnapshot(format) {
|
||||
snapshot(prettier.format(makeBadge(format), { parser: 'html' }))
|
||||
}
|
||||
|
||||
function testColor(color = '', colorAttr = 'color') {
|
||||
@@ -17,7 +17,7 @@ function testColor(color = '', colorAttr = 'color') {
|
||||
message: 'Bob',
|
||||
[colorAttr]: color,
|
||||
format: 'json',
|
||||
}),
|
||||
})
|
||||
).color
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ describe('The badge generator', function () {
|
||||
given('bluish'),
|
||||
given('almostred'),
|
||||
given('brightmaroon'),
|
||||
given('cactus'),
|
||||
given('cactus')
|
||||
).expect(undefined)
|
||||
})
|
||||
})
|
||||
@@ -87,8 +87,8 @@ describe('The badge generator', function () {
|
||||
.and.to.include('grown')
|
||||
})
|
||||
|
||||
it('should match snapshot', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshot', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -138,14 +138,14 @@ describe('The badge generator', function () {
|
||||
message: 'Bob',
|
||||
format: 'svg',
|
||||
style: 'unknown_style',
|
||||
}),
|
||||
})
|
||||
).to.throw(Error, "Unknown badge style: 'unknown_style'")
|
||||
})
|
||||
})
|
||||
|
||||
describe('"flat" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -155,8 +155,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -167,8 +167,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -177,8 +177,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -188,8 +188,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -200,8 +200,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -212,8 +212,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -223,8 +223,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -236,8 +236,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('"flat-square" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -247,8 +247,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -259,8 +259,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -269,8 +269,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -280,8 +280,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -292,8 +292,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -304,8 +304,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -315,8 +315,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -328,8 +328,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('"plastic" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -339,8 +339,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -351,8 +351,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -361,8 +361,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -372,8 +372,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -384,8 +384,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -396,8 +396,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -407,8 +407,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -428,7 +428,7 @@ describe('The badge generator', function () {
|
||||
message: 1999,
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('1998')
|
||||
.and.to.include('1999')
|
||||
@@ -441,14 +441,14 @@ describe('The badge generator', function () {
|
||||
message: '1 string',
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('LABEL')
|
||||
.and.to.include('1 STRING')
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -458,8 +458,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -470,8 +470,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -480,8 +480,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -491,8 +491,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -503,8 +503,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -515,8 +515,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -526,8 +526,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -546,7 +546,7 @@ describe('The badge generator', function () {
|
||||
message: 'some-value',
|
||||
format: 'svg',
|
||||
style: 'social',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('Some-key')
|
||||
.and.to.include('some-value')
|
||||
@@ -560,14 +560,14 @@ describe('The badge generator', function () {
|
||||
message: 'some-value',
|
||||
format: 'json',
|
||||
style: 'social',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('""')
|
||||
.and.to.include('some-value')
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -577,8 +577,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -589,8 +589,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -599,8 +599,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -610,8 +610,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -622,8 +622,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -636,8 +636,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('badges with logos should always produce the same badge', function () {
|
||||
it('badge with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('badge with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'label',
|
||||
message: 'message',
|
||||
format: 'svg',
|
||||
|
||||
@@ -66,7 +66,7 @@ class XmlElement {
|
||||
})
|
||||
.join(' ')
|
||||
return stripXmlWhitespace(
|
||||
`<${this.name}${attrsStr}>${content}</${this.name}>`,
|
||||
`<${this.name}${attrsStr}>${content}</${this.name}>`
|
||||
)
|
||||
}
|
||||
return stripXmlWhitespace(`<${this.name}${attrsStr}/>`)
|
||||
@@ -88,7 +88,7 @@ class ElementList {
|
||||
typeof el.render === 'function'
|
||||
? acc + el.render()
|
||||
: acc + escapeXml(el),
|
||||
'',
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"badge": "lib/badge-cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">= 14",
|
||||
"npm": ">= 6"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('Badge URL helper functions', function () {
|
||||
given('single trailing underscore_').expect('single trailing underscore ')
|
||||
given('__double leading underscores').expect('_double leading underscores')
|
||||
given('double trailing underscores__').expect(
|
||||
'double trailing underscores_',
|
||||
'double trailing underscores_'
|
||||
)
|
||||
given('treble___underscores').expect('treble_ underscores')
|
||||
given('fourfold____underscores').expect('fourfold__underscores')
|
||||
|
||||
@@ -11,7 +11,7 @@ class AuthHelper {
|
||||
isRequired = false,
|
||||
defaultToEmptyStringForUser = false,
|
||||
},
|
||||
config,
|
||||
config
|
||||
) {
|
||||
if (!userKey && !passKey) {
|
||||
throw Error('Expected userKey or passKey to be set')
|
||||
@@ -142,7 +142,7 @@ class AuthHelper {
|
||||
|
||||
withBasicAuth(requestParams) {
|
||||
return this._withAnyAuth(requestParams, requestParams =>
|
||||
this.constructor._mergeAuth(requestParams, this._basicAuth),
|
||||
this.constructor._mergeAuth(requestParams, this._basicAuth)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,13 +172,13 @@ class AuthHelper {
|
||||
|
||||
withBearerAuthHeader(
|
||||
requestParams,
|
||||
bearerKey = 'Bearer', // lgtm [js/hardcoded-credentials]
|
||||
bearerKey = 'Bearer' // lgtm [js/hardcoded-credentials]
|
||||
) {
|
||||
return this._withAnyAuth(requestParams, requestParams =>
|
||||
this.constructor._mergeHeaders(
|
||||
requestParams,
|
||||
this._bearerAuthHeader(bearerKey),
|
||||
),
|
||||
this._bearerAuthHeader(bearerKey)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ class AuthHelper {
|
||||
this.constructor._mergeQueryParams(requestParams, {
|
||||
...(userKey ? { [userKey]: this._user } : undefined),
|
||||
...(passKey ? { [passKey]: this._pass } : undefined),
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ describe('AuthHelper', function () {
|
||||
it('throws without userKey or passKey', function () {
|
||||
expect(() => new AuthHelper({}, {})).to.throw(
|
||||
Error,
|
||||
'Expected userKey or passKey to be set',
|
||||
'Expected userKey or passKey to be set'
|
||||
)
|
||||
})
|
||||
it('throws without serviceKey or authorizedOrigins', function () {
|
||||
expect(
|
||||
() =>
|
||||
new AuthHelper({ userKey: 'myci_user', passKey: 'myci_pass' }, {}),
|
||||
() => new AuthHelper({ userKey: 'myci_user', passKey: 'myci_pass' }, {})
|
||||
).to.throw(Error, 'Expected authorizedOrigins or serviceKey to be set')
|
||||
})
|
||||
it('throws when authorizedOrigins is not an array', function () {
|
||||
@@ -26,8 +25,8 @@ describe('AuthHelper', function () {
|
||||
passKey: 'myci_pass',
|
||||
authorizedOrigins: true,
|
||||
},
|
||||
{ private: {} },
|
||||
),
|
||||
{ private: {} }
|
||||
)
|
||||
).to.throw(Error, 'Expected authorizedOrigins to be an array of origins')
|
||||
})
|
||||
})
|
||||
@@ -36,7 +35,7 @@ describe('AuthHelper', function () {
|
||||
function validate(config, privateConfig) {
|
||||
return new AuthHelper(
|
||||
{ authorizedOrigins: ['https://example.test'], ...config },
|
||||
{ private: privateConfig },
|
||||
{ private: privateConfig }
|
||||
).isValid
|
||||
}
|
||||
test(validate, () => {
|
||||
@@ -44,20 +43,20 @@ describe('AuthHelper', function () {
|
||||
// Fully configured user + pass.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
// Fully configured user or pass.
|
||||
given(
|
||||
{ userKey: 'myci_user', isRequired: true },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
given(
|
||||
{ passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_pass: 'abc123' },
|
||||
{ myci_pass: 'abc123' }
|
||||
),
|
||||
given({ userKey: 'myci_user' }, { myci_user: 'admin' }),
|
||||
given({ passKey: 'myci_pass' }, { myci_pass: 'abc123' }),
|
||||
@@ -71,16 +70,16 @@ describe('AuthHelper', function () {
|
||||
// Partly configured.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
// Missing required config.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{},
|
||||
{}
|
||||
),
|
||||
given({ userKey: 'myci_user', isRequired: true }, {}),
|
||||
given({ passKey: 'myci_pass', isRequired: true }, {}),
|
||||
@@ -92,18 +91,18 @@ describe('AuthHelper', function () {
|
||||
function validate(config, privateConfig) {
|
||||
return new AuthHelper(
|
||||
{ authorizedOrigins: ['https://example.test'], ...config },
|
||||
{ private: privateConfig },
|
||||
{ private: privateConfig }
|
||||
)._basicAuth
|
||||
}
|
||||
test(validate, () => {
|
||||
forCases([
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
]).expect({ username: 'admin', password: 'abc123' })
|
||||
given({ userKey: 'myci_user' }, { myci_user: 'admin' }).expect({
|
||||
@@ -115,11 +114,11 @@ describe('AuthHelper', function () {
|
||||
password: 'abc123',
|
||||
})
|
||||
given({ userKey: 'myci_user', passKey: 'myci_pass' }, {}).expect(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
given(
|
||||
{ passKey: 'myci_pass', defaultToEmptyStringForUser: true },
|
||||
{ myci_pass: 'abc123' },
|
||||
{ myci_pass: 'abc123' }
|
||||
).expect({
|
||||
username: '',
|
||||
password: 'abc123',
|
||||
@@ -169,7 +168,7 @@ describe('AuthHelper', function () {
|
||||
expect(() =>
|
||||
authHelper.enforceStrictSsl({
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).to.throw(InvalidParameter)
|
||||
})
|
||||
})
|
||||
@@ -193,7 +192,7 @@ describe('AuthHelper', function () {
|
||||
expect(() =>
|
||||
authHelper.enforceStrictSsl({
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).not.to.throw()
|
||||
})
|
||||
})
|
||||
@@ -319,7 +318,7 @@ describe('AuthHelper', function () {
|
||||
},
|
||||
},
|
||||
private: { myci_user: 'admin', myci_pass: 'abc123' },
|
||||
},
|
||||
}
|
||||
)
|
||||
const withBasicAuth = requestOptions =>
|
||||
authHelper.withBasicAuth(requestOptions)
|
||||
@@ -377,7 +376,7 @@ describe('AuthHelper', function () {
|
||||
withBasicAuth({
|
||||
url: 'https://myci.test/api',
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).to.throw(InvalidParameter)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -91,7 +91,7 @@ class BaseGraphqlService extends BaseService {
|
||||
throw exception
|
||||
} else {
|
||||
throw Error(
|
||||
`transformErrors() must return a ShieldsRuntimeError; got ${exception}`,
|
||||
`transformErrors() must return a ShieldsRuntimeError; got ${exception}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,23 +35,23 @@ describe('BaseGraphqlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '{"some": "json"}',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/graphql',
|
||||
{
|
||||
body: '{"query":"{\\n requiredString\\n}","variables":{}}',
|
||||
body: '{"query":"{\\n requiredString\\n}\\n","variables":{}}',
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -74,17 +74,17 @@ describe('BaseGraphqlService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/graphql',
|
||||
{
|
||||
body: '{"query":"{\\n requiredString\\n}","variables":{}}',
|
||||
body: '{"query":"{\\n requiredString\\n}\\n","variables":{}}',
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -113,8 +113,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -130,8 +130,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -149,8 +149,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -188,8 +188,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await WithErrorHandler.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -28,21 +28,21 @@ describe('BaseJsonService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '{"some": "json"}',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.json',
|
||||
{
|
||||
headers: { Accept: 'application/json' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('BaseJsonService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -69,7 +69,7 @@ describe('BaseJsonService', function () {
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -83,8 +83,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -115,8 +115,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -33,14 +33,14 @@ export default class BaseStaticService extends BaseService {
|
||||
{},
|
||||
serviceConfig,
|
||||
namedParams,
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
|
||||
const badgeData = coalesceBadge(
|
||||
queryParams,
|
||||
serviceData,
|
||||
this.defaultBadgeData,
|
||||
this,
|
||||
this
|
||||
)
|
||||
|
||||
// The final capture group is the extension.
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
describe('valueFromSvgBadge', function () {
|
||||
it('should find the correct value', function () {
|
||||
expect(BaseSvgScrapingService.valueFromSvgBadge(exampleSvg)).to.equal(
|
||||
exampleMessage,
|
||||
exampleMessage
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -40,21 +40,21 @@ describe('BaseSvgScrapingService', function () {
|
||||
Promise.resolve({
|
||||
buffer: exampleSvg,
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher with the expected header', async function () {
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.svg',
|
||||
{
|
||||
headers: { Accept: 'image/svg+xml' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
|
||||
await WithCustomOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -84,7 +84,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
method: 'POST',
|
||||
headers: { Accept: 'image/svg+xml' },
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: exampleMessage,
|
||||
})
|
||||
@@ -124,8 +124,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await WithValueMatcher.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'a different message',
|
||||
})
|
||||
@@ -139,8 +139,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -28,21 +28,21 @@ describe('BaseXmlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '<requiredString>some-string</requiredString>',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.xml',
|
||||
{
|
||||
headers: { Accept: 'application/xml, text/xml' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('BaseXmlService', function () {
|
||||
|
||||
await WithCustomOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -71,7 +71,7 @@ describe('BaseXmlService', function () {
|
||||
headers: { Accept: 'application/xml, text/xml' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -85,8 +85,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -112,8 +112,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlServiceWithParserOption.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string with trailing whitespace ',
|
||||
})
|
||||
@@ -127,8 +127,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -144,8 +144,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -44,14 +44,14 @@ describe('BaseYamlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: expectedYaml,
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -61,7 +61,7 @@ describe('BaseYamlService', function () {
|
||||
Accept:
|
||||
'text/x-yaml, text/yaml, application/x-yaml, application/yaml, text/plain',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('BaseYamlService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -91,7 +91,7 @@ describe('BaseYamlService', function () {
|
||||
},
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -105,8 +105,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -120,8 +120,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -137,8 +137,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -44,7 +44,7 @@ const optionalStringWhenNamedLogoPresent = Joi.alternatives().conditional(
|
||||
{
|
||||
is: Joi.string().required(),
|
||||
then: Joi.string(),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const optionalNumberWhenAnyLogoPresent = Joi.alternatives()
|
||||
@@ -183,28 +183,12 @@ class BaseService {
|
||||
Joi.assert(
|
||||
this.defaultBadgeData,
|
||||
defaultBadgeDataSchema,
|
||||
`Default badge data for ${this.name}`,
|
||||
`Default badge data for ${this.name}`
|
||||
)
|
||||
|
||||
this.examples.forEach((example, index) =>
|
||||
validateExample(example, index, this),
|
||||
validateExample(example, index, this)
|
||||
)
|
||||
|
||||
// ensure openApi spec matches route
|
||||
if (this.openApi) {
|
||||
const preparedRoute = prepareRoute(this.route)
|
||||
for (const [key, value] of Object.entries(this.openApi)) {
|
||||
let example = key
|
||||
for (const param of value.get.parameters) {
|
||||
example = example.replace(`{${param.name}}`, param.example)
|
||||
}
|
||||
if (!example.match(preparedRoute.regex)) {
|
||||
throw new Error(
|
||||
`Inconsistent Open Api spec and Route found for service ${this.name}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDefinition() {
|
||||
@@ -213,7 +197,7 @@ class BaseService {
|
||||
const queryParams = getQueryParamNames(this.route)
|
||||
|
||||
const examples = this.examples.map((example, index) =>
|
||||
transformExample(example, index, this),
|
||||
transformExample(example, index, this)
|
||||
)
|
||||
|
||||
let route
|
||||
@@ -234,7 +218,7 @@ class BaseService {
|
||||
|
||||
constructor(
|
||||
{ requestFetcher, authHelper, metricHelper },
|
||||
{ handleInternalErrors },
|
||||
{ handleInternalErrors }
|
||||
) {
|
||||
this._requestFetcher = requestFetcher
|
||||
this.authHelper = authHelper
|
||||
@@ -250,9 +234,9 @@ class BaseService {
|
||||
const params = new URLSearchParams(
|
||||
Object.fromEntries(
|
||||
Object.entries(options.searchParams).filter(
|
||||
([k, v]) => v !== undefined,
|
||||
),
|
||||
),
|
||||
([k, v]) => v !== undefined
|
||||
)
|
||||
)
|
||||
)
|
||||
logUrl = `${url}?${params.toString()}`
|
||||
delete logOptions.searchParams
|
||||
@@ -260,12 +244,12 @@ class BaseService {
|
||||
logTrace(
|
||||
emojic.bowAndArrow,
|
||||
'Request',
|
||||
`${logUrl}\n${JSON.stringify(logOptions, null, 2)}`,
|
||||
`${logUrl}\n${JSON.stringify(logOptions, null, 2)}`
|
||||
)
|
||||
const { res, buffer } = await this._requestFetcher(
|
||||
url,
|
||||
options,
|
||||
systemErrors,
|
||||
systemErrors
|
||||
)
|
||||
await this._meterResponse(res, buffer)
|
||||
logTrace(emojic.dart, 'Response status code', res.statusCode)
|
||||
@@ -295,7 +279,7 @@ class BaseService {
|
||||
prettyErrorMessage = 'invalid response data',
|
||||
includeKeys = false,
|
||||
allowAndStripUnknownKeys = true,
|
||||
} = {},
|
||||
} = {}
|
||||
) {
|
||||
return validate(
|
||||
{
|
||||
@@ -307,7 +291,7 @@ class BaseService {
|
||||
allowAndStripUnknownKeys,
|
||||
},
|
||||
data,
|
||||
schema,
|
||||
schema
|
||||
)
|
||||
}
|
||||
|
||||
@@ -363,7 +347,7 @@ class BaseService {
|
||||
'unhandledError',
|
||||
emojic.boom,
|
||||
'Unhandled internal error',
|
||||
error,
|
||||
error
|
||||
)
|
||||
) {
|
||||
// This is where we end up if an unhandled exception is thrown in
|
||||
@@ -381,7 +365,7 @@ class BaseService {
|
||||
'unhandledError',
|
||||
emojic.boom,
|
||||
'Unhandled internal error',
|
||||
error,
|
||||
error
|
||||
)
|
||||
throw error
|
||||
}
|
||||
@@ -391,7 +375,7 @@ class BaseService {
|
||||
context = {},
|
||||
config = {},
|
||||
namedParams = {},
|
||||
queryParams = {},
|
||||
queryParams = {}
|
||||
) {
|
||||
trace.logTrace('inbound', emojic.womanCook, 'Service class', this.name)
|
||||
trace.logTrace('inbound', emojic.ticket, 'Named params', namedParams)
|
||||
@@ -425,13 +409,13 @@ class BaseService {
|
||||
traceSuccessMessage: 'Query params after validation',
|
||||
},
|
||||
queryParams,
|
||||
queryParamSchema,
|
||||
queryParamSchema
|
||||
)
|
||||
trace.logTrace(
|
||||
'inbound',
|
||||
emojic.crayon,
|
||||
'Query params after validation',
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
} catch (error) {
|
||||
serviceError = error
|
||||
@@ -445,7 +429,7 @@ class BaseService {
|
||||
try {
|
||||
serviceData = await serviceInstance.handle(
|
||||
namedParams,
|
||||
transformedQueryParams,
|
||||
transformedQueryParams
|
||||
)
|
||||
serviceInstance._validateServiceData(serviceData)
|
||||
} catch (error) {
|
||||
@@ -470,7 +454,7 @@ class BaseService {
|
||||
librariesIoApiProvider,
|
||||
metricInstance,
|
||||
},
|
||||
serviceConfig,
|
||||
serviceConfig
|
||||
) {
|
||||
const { cacheHeaders: cacheHeaderConfig } = serviceConfig
|
||||
const { regex, captureNames } = prepareRoute(this.route)
|
||||
@@ -498,14 +482,14 @@ class BaseService {
|
||||
},
|
||||
serviceConfig,
|
||||
namedParams,
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
|
||||
const badgeData = coalesceBadge(
|
||||
queryParams,
|
||||
serviceData,
|
||||
this.defaultBadgeData,
|
||||
this,
|
||||
this
|
||||
)
|
||||
// The final capture group is the extension.
|
||||
const format = (match.slice(-1)[0] || '.svg').replace(/^\./, '')
|
||||
@@ -514,7 +498,7 @@ class BaseService {
|
||||
metricHandle.noteResponseSent()
|
||||
},
|
||||
cacheLength: this._cacheLength,
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: '!' },
|
||||
),
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'Hello namedParamA: bar.bar.bar with queryParamA: !',
|
||||
})
|
||||
@@ -85,8 +85,8 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: ['foo', 'bar'] },
|
||||
),
|
||||
{ queryParamA: ['foo', 'bar'] }
|
||||
)
|
||||
).to.deep.equal({
|
||||
color: 'red',
|
||||
isError: true,
|
||||
@@ -97,13 +97,13 @@ describe('BaseService', function () {
|
||||
describe('Required overrides', function () {
|
||||
it('Should throw if render() is not overridden', function () {
|
||||
expect(() => BaseService.render()).to.throw(
|
||||
/^render\(\) function not implemented for BaseService$/,
|
||||
/^render\(\) function not implemented for BaseService$/
|
||||
)
|
||||
})
|
||||
|
||||
it('Should throw if route is not overridden', function () {
|
||||
return expect(BaseService.invoke({}, {}, {})).to.be.rejectedWith(
|
||||
/^Route not defined for BaseService$/,
|
||||
/^Route not defined for BaseService$/
|
||||
)
|
||||
})
|
||||
|
||||
@@ -112,13 +112,13 @@ describe('BaseService', function () {
|
||||
}
|
||||
it('Should throw if handle() is not overridden', function () {
|
||||
return expect(WithRoute.invoke({}, {}, {})).to.be.rejectedWith(
|
||||
/^Handler not implemented for WithRoute$/,
|
||||
/^Handler not implemented for WithRoute$/
|
||||
)
|
||||
})
|
||||
|
||||
it('Should throw if category is not overridden', function () {
|
||||
expect(() => BaseService.category).to.throw(
|
||||
/^Category not set for BaseService$/,
|
||||
/^Category not set for BaseService$/
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -135,25 +135,25 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: '!' },
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWithMatch(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Service class',
|
||||
'DummyService',
|
||||
'DummyService'
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWith(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Named params',
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWith(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Query params after validation',
|
||||
{ queryParamA: '!' },
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -171,7 +171,7 @@ describe('BaseService', function () {
|
||||
const serviceData = await LinkService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
|
||||
expect(serviceData).to.deep.equal({
|
||||
@@ -194,7 +194,7 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
@@ -212,7 +212,7 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
@@ -233,8 +233,8 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: true },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
),
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -251,7 +251,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'red',
|
||||
@@ -266,7 +266,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -281,7 +281,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -296,7 +296,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -311,7 +311,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'red',
|
||||
@@ -336,7 +336,7 @@ describe('BaseService', function () {
|
||||
mockHandleRequest = sinon.spy()
|
||||
DummyService.register(
|
||||
{ camp: mockCamp, handleRequest: mockHandleRequest },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
})
|
||||
|
||||
@@ -413,8 +413,8 @@ describe('BaseService', function () {
|
||||
expect(() =>
|
||||
DummyService._validate(
|
||||
{ requiredString: ['this', "shouldn't", 'work'] },
|
||||
dummySchema,
|
||||
),
|
||||
dummySchema
|
||||
)
|
||||
)
|
||||
.to.throw()
|
||||
.instanceof(InvalidResponse)
|
||||
@@ -436,7 +436,7 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
const url = 'some-url'
|
||||
@@ -453,14 +453,14 @@ describe('BaseService', function () {
|
||||
`${url}?param1=foobar\n${JSON.stringify(
|
||||
{ headers: options.headers },
|
||||
null,
|
||||
2,
|
||||
)}`,
|
||||
2
|
||||
)}`
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWithMatch(
|
||||
'fetch',
|
||||
sinon.match.string,
|
||||
'Response status code',
|
||||
200,
|
||||
200
|
||||
)
|
||||
})
|
||||
|
||||
@@ -471,7 +471,7 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -504,17 +504,17 @@ describe('BaseService', function () {
|
||||
const serviceInstance =
|
||||
new DummyServiceWithServiceResponseSizeMetricEnabled(
|
||||
{ requestFetcher, metricHelper },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
await serviceInstance._request({ url })
|
||||
|
||||
expect(await register.getSingleMetricAsString('service_response_bytes'))
|
||||
.to.contain(
|
||||
'service_response_bytes_bucket{le="65536",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 0\n',
|
||||
'service_response_bytes_bucket{le="65536",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 0\n'
|
||||
)
|
||||
.and.to.contain(
|
||||
'service_response_bytes_bucket{le="131072",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 1\n',
|
||||
'service_response_bytes_bucket{le="131072",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 1\n'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -529,13 +529,13 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher, metricHelper },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
await serviceInstance._request({ url })
|
||||
|
||||
expect(
|
||||
await register.getSingleMetricAsString('service_response_bytes'),
|
||||
await register.getSingleMetricAsString('service_response_bytes')
|
||||
).to.not.contain('service_response_bytes_bucket')
|
||||
})
|
||||
})
|
||||
@@ -565,8 +565,8 @@ describe('BaseService', function () {
|
||||
},
|
||||
private: { myci_pass: 'abc123' },
|
||||
},
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
),
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
).to.deep.equal({ message: 'The CI password is abc123' })
|
||||
})
|
||||
|
||||
@@ -583,8 +583,8 @@ describe('BaseService', function () {
|
||||
},
|
||||
{
|
||||
namedParamA: 'bar.bar.bar',
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
).to.deep.equal({
|
||||
color: 'lightgray',
|
||||
isError: true,
|
||||
|
||||
@@ -41,7 +41,7 @@ function coalesceCacheLength({
|
||||
const cacheLength = coalesce(
|
||||
serviceOverrideCacheLengthSeconds,
|
||||
serviceDefaultCacheLengthSeconds,
|
||||
defaultCacheLengthSeconds,
|
||||
defaultCacheLengthSeconds
|
||||
)
|
||||
|
||||
// Overrides can apply _more_ caching, but not less. Query param overriding
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
'no-cache, no-store, must-revalidate',
|
||||
'no-cache, no-store, must-revalidate'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -141,7 +141,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
'max-age=123, s-maxage=123',
|
||||
'max-age=123, s-maxage=123'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -156,7 +156,7 @@ describe('Cache header functions', function () {
|
||||
it('sets the expected fields', function () {
|
||||
const expectedFields = ['date', 'cache-control', 'expires']
|
||||
expectedFields.forEach(field =>
|
||||
expect(res._headers[field]).to.equal(undefined),
|
||||
expect(res._headers[field]).to.equal(undefined)
|
||||
)
|
||||
|
||||
setCacheHeaders({
|
||||
@@ -169,7 +169,7 @@ describe('Cache header functions', function () {
|
||||
expectedFields.forEach(field =>
|
||||
expect(res._headers[field])
|
||||
.to.be.a('string')
|
||||
.and.have.lengthOf.at.least(1),
|
||||
.and.have.lengthOf.at.least(1)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -181,7 +181,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
`max-age=${24 * 3600}, s-maxage=${24 * 3600}`,
|
||||
`max-age=${24 * 3600}, s-maxage=${24 * 3600}`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -190,7 +190,7 @@ describe('Cache header functions', function () {
|
||||
expect(new Date(lastModified)).to.be.withinTime(
|
||||
// Within the last 60 seconds.
|
||||
new Date(Date.now() - 60 * 1000),
|
||||
new Date(),
|
||||
new Date()
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -221,7 +221,7 @@ describe('Cache header functions', function () {
|
||||
})
|
||||
expect(serverHasBeenUpSinceResourceCached(req)).to.equal(false)
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
context(
|
||||
'when the If-Modified-Since header is after the process started',
|
||||
@@ -233,7 +233,7 @@ describe('Cache header functions', function () {
|
||||
})
|
||||
expect(serverHasBeenUpSinceResourceCached(req)).to.equal(true)
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function checkErrorResponse(httpErrors = {}) {
|
||||
error = new NotFound({ prettyMessage: httpErrors[404] })
|
||||
} else if (res.statusCode !== 200) {
|
||||
const underlying = Error(
|
||||
`Got status code ${res.statusCode} (expected 200)`,
|
||||
`Got status code ${res.statusCode} (expected 200)`
|
||||
)
|
||||
const props = { underlyingError: underlying }
|
||||
if (httpErrors[res.statusCode] !== undefined) {
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 429 (expected 200)',
|
||||
'Invalid Response: Got status code 429 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('rate limited by upstream service')
|
||||
expect(e.response).to.equal(res)
|
||||
@@ -72,10 +72,10 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 429 (expected 200)',
|
||||
'Invalid Response: Got status code 429 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal(
|
||||
"terribly sorry but that's one too many requests",
|
||||
"terribly sorry but that's one too many requests"
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -90,7 +90,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 499 (expected 200)',
|
||||
'Invalid Response: Got status code 499 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('invalid')
|
||||
expect(e.response).to.equal(res)
|
||||
@@ -106,7 +106,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 403 (expected 200)',
|
||||
'Invalid Response: Got status code 403 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('access denied')
|
||||
}
|
||||
@@ -122,7 +122,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(Inaccessible)
|
||||
expect(e.message).to.equal(
|
||||
'Inaccessible: Got status code 503 (expected 200)',
|
||||
'Inaccessible: Got status code 503 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('inaccessible')
|
||||
expect(e.response).to.equal(res)
|
||||
@@ -138,7 +138,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(Inaccessible)
|
||||
expect(e.message).to.equal(
|
||||
'Inaccessible: Got status code 500 (expected 200)',
|
||||
'Inaccessible: Got status code 500 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('server overloaded')
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function coalesceBadge(
|
||||
serviceData,
|
||||
// These two parameters were kept separate to make tests clearer.
|
||||
defaultBadgeData,
|
||||
{ category, _cacheLength: defaultCacheSeconds } = {},
|
||||
{ category, _cacheLength: defaultCacheSeconds } = {}
|
||||
) {
|
||||
// The "overrideX" naming is based on services that provide badge
|
||||
// parameters themselves, which can be overridden by a query string
|
||||
@@ -141,7 +141,7 @@ export default function coalesceBadge(
|
||||
} else {
|
||||
namedLogo = coalesce(
|
||||
serviceNamedLogo,
|
||||
style === 'social' ? defaultNamedLogo : undefined,
|
||||
style === 'social' ? defaultNamedLogo : undefined
|
||||
)
|
||||
namedLogoColor = coalesce(overrideLogoColor, serviceLogoColor)
|
||||
}
|
||||
@@ -166,13 +166,13 @@ export default function coalesceBadge(
|
||||
isError ? undefined : overrideColor,
|
||||
serviceColor,
|
||||
defaultColor,
|
||||
'lightgrey',
|
||||
'lightgrey'
|
||||
),
|
||||
labelColor: coalesce(
|
||||
// In case of an error, disregard user's color override.
|
||||
isError ? undefined : overrideLabelColor,
|
||||
serviceLabelColor,
|
||||
defaultLabelColor,
|
||||
defaultLabelColor
|
||||
),
|
||||
style,
|
||||
namedLogo,
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the label', function () {
|
||||
expect(
|
||||
coalesceBadge({ label: 'purr count' }, { label: 'purrs' }, {}),
|
||||
coalesceBadge({ label: 'purr count' }, { label: 'purrs' }, {})
|
||||
).to.include({ label: 'purr count' })
|
||||
})
|
||||
})
|
||||
@@ -54,11 +54,11 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the color', function () {
|
||||
expect(
|
||||
coalesceBadge({ color: '10ADED' }, { color: 'red' }, {}),
|
||||
coalesceBadge({ color: '10ADED' }, { color: 'red' }, {})
|
||||
).to.include({ color: '10ADED' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge({ colorB: 'B0ADED' }, { color: 'red' }, {}),
|
||||
coalesceBadge({ colorB: 'B0ADED' }, { color: 'red' }, {})
|
||||
).to.include({ color: 'B0ADED' })
|
||||
})
|
||||
|
||||
@@ -68,16 +68,16 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ color: '10ADED' },
|
||||
{ isError: true, color: 'lightgray' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: 'lightgray' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ colorB: 'B0ADED' },
|
||||
{ isError: true, color: 'lightgray' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: 'lightgray' })
|
||||
})
|
||||
})
|
||||
@@ -102,11 +102,11 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the label color', function () {
|
||||
expect(
|
||||
coalesceBadge({ labelColor: '42f483' }, { color: 'green' }, {}),
|
||||
coalesceBadge({ labelColor: '42f483' }, { color: 'green' }, {})
|
||||
).to.include({ labelColor: '42f483' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge({ colorA: 'B2f483' }, { color: 'green' }, {}),
|
||||
coalesceBadge({ colorA: 'B2f483' }, { color: 'green' }, {})
|
||||
).to.include({ labelColor: 'B2f483' })
|
||||
})
|
||||
|
||||
@@ -116,8 +116,8 @@ describe('coalesceBadge', function () {
|
||||
// Scoutcamp converts numeric query params to numbers.
|
||||
{ color: 123 },
|
||||
{ color: 'green' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: '123' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
@@ -125,8 +125,8 @@ describe('coalesceBadge', function () {
|
||||
// Scoutcamp converts numeric query params to numbers.
|
||||
{ colorB: 123 },
|
||||
{ color: 'green' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: '123' })
|
||||
})
|
||||
})
|
||||
@@ -140,7 +140,7 @@ describe('coalesceBadge', function () {
|
||||
it('when a social badge, uses the default named logo', function () {
|
||||
// .not.be.empty for confidence that nothing has changed with `getShieldsIcon()`.
|
||||
expect(
|
||||
coalesceBadge({ style: 'social' }, {}, { namedLogo: 'appveyor' }).logo,
|
||||
coalesceBadge({ style: 'social' }, {}, { namedLogo: 'appveyor' }).logo
|
||||
).to.equal(getSimpleIcon({ name: 'appveyor' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -149,28 +149,28 @@ describe('coalesceBadge', function () {
|
||||
namedLogo: 'npm',
|
||||
})
|
||||
expect(coalesceBadge({}, { namedLogo: 'npm' }, {}).logo).to.equal(
|
||||
getShieldsIcon({ name: 'npm' }),
|
||||
getShieldsIcon({ name: 'npm' })
|
||||
).and.not.to.be.empty
|
||||
})
|
||||
|
||||
it('applies the named monochrome logo with color', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'dependabot', logoColor: 'blue' }, {})
|
||||
.logo,
|
||||
.logo
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.to.be.empty
|
||||
})
|
||||
|
||||
it('applies the named multicolored logo with color', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoColor: 'blue' }, {}).logo,
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoColor: 'blue' }, {}).logo
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.to.be
|
||||
.empty
|
||||
})
|
||||
|
||||
it('overrides the logo', function () {
|
||||
expect(
|
||||
coalesceBadge({ logo: 'npm' }, { namedLogo: 'appveyor' }, {}).logo,
|
||||
coalesceBadge({ logo: 'npm' }, { namedLogo: 'appveyor' }, {}).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -179,8 +179,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logo: 'dependabot', logoColor: 'blue' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.be.empty
|
||||
})
|
||||
@@ -190,8 +190,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logo: 'npm', logoColor: 'blue' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -205,8 +205,8 @@ describe('coalesceBadge', function () {
|
||||
logoPosition: -3,
|
||||
logoWidth: 100,
|
||||
},
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -215,8 +215,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logoColor: 'blue' },
|
||||
{ namedLogo: 'dependabot', logoColor: 'red' },
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.be.empty
|
||||
})
|
||||
@@ -226,8 +226,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logoColor: 'blue' },
|
||||
{ namedLogo: 'npm', logoColor: 'red' },
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -235,7 +235,7 @@ describe('coalesceBadge', function () {
|
||||
it('overrides logoSvg', function () {
|
||||
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
|
||||
expect(coalesceBadge({ logo: 'npm' }, { logoSvg }, {}).logo).to.equal(
|
||||
getShieldsIcon({ name: 'npm' }),
|
||||
getShieldsIcon({ name: 'npm' })
|
||||
).and.not.be.empty
|
||||
})
|
||||
})
|
||||
@@ -244,7 +244,7 @@ describe('coalesceBadge', function () {
|
||||
it('overrides the logo with custom svg', function () {
|
||||
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
|
||||
expect(
|
||||
coalesceBadge({ logo: logoSvg }, { namedLogo: 'appveyor' }, {}),
|
||||
coalesceBadge({ logo: logoSvg }, { namedLogo: 'appveyor' }, {})
|
||||
).to.include({ logo: logoSvg })
|
||||
})
|
||||
|
||||
@@ -254,8 +254,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logo: logoSvg, logoColor: 'brightgreen' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ logo: logoSvg })
|
||||
})
|
||||
})
|
||||
@@ -269,7 +269,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('applies the logo width', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoWidth: 275 }, {}),
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoWidth: 275 }, {})
|
||||
).to.include({ logoWidth: 275 })
|
||||
})
|
||||
})
|
||||
@@ -283,7 +283,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('applies the logo position', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoPosition: -10 }, {}),
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoPosition: -10 }, {})
|
||||
).to.include({ logoPosition: -10 })
|
||||
})
|
||||
})
|
||||
@@ -296,8 +296,8 @@ describe('coalesceBadge', function () {
|
||||
{
|
||||
link: 'https://circleci.com/workflow-run/184ef3de-4836-4805-a2e4-0ceba099f92d',
|
||||
},
|
||||
{},
|
||||
).links,
|
||||
{}
|
||||
).links
|
||||
).to.deep.equal(['https://circleci.com/gh/badges/daily-tests'])
|
||||
})
|
||||
})
|
||||
@@ -328,7 +328,7 @@ describe('coalesceBadge', function () {
|
||||
describe('Cache length', function () {
|
||||
it('overrides the cache length', function () {
|
||||
expect(
|
||||
coalesceBadge({ style: 'pill' }, { cacheSeconds: 123 }, {}),
|
||||
coalesceBadge({ style: 'pill' }, { cacheSeconds: 123 }, {})
|
||||
).to.include({ cacheLengthSeconds: 123 })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ function deprecatedService(attrs) {
|
||||
const { route, name, label, category, examples, message } = Joi.attempt(
|
||||
attrs,
|
||||
attrSchema,
|
||||
`Deprecated service for ${attrs.route.base}`,
|
||||
`Deprecated service for ${attrs.route.base}`
|
||||
)
|
||||
|
||||
return class DeprecatedService extends BaseService {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { makeFullUrl } from './route.js'
|
||||
|
||||
const optionalObjectOfKeyValues = Joi.object().pattern(
|
||||
/./,
|
||||
Joi.string().allow(null),
|
||||
Joi.string().allow(null)
|
||||
)
|
||||
|
||||
const schema = Joi.object({
|
||||
@@ -32,19 +32,19 @@ function validateExample(example, index, ServiceClass) {
|
||||
const result = Joi.attempt(
|
||||
example,
|
||||
schema,
|
||||
`Example for ${ServiceClass.name} at index ${index}`,
|
||||
`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`,
|
||||
`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`,
|
||||
`Example for ${ServiceClass.name} at index ${index} declares a redundant pattern which should be removed`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function validateExample(example, index, ServiceClass) {
|
||||
throw Error(
|
||||
`In example for ${
|
||||
ServiceClass.name
|
||||
} at index ${index}, ${e.message.toLowerCase()}`,
|
||||
} at index ${index}, ${e.message.toLowerCase()}`
|
||||
)
|
||||
}
|
||||
// Make sure there are no extra keys.
|
||||
@@ -73,8 +73,8 @@ function validateExample(example, index, ServiceClass) {
|
||||
`In example for ${
|
||||
ServiceClass.name
|
||||
} at index ${index}, namedParams contains unknown keys: ${extraKeys.join(
|
||||
', ',
|
||||
)}`,
|
||||
', '
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -86,22 +86,22 @@ function validateExample(example, index, ServiceClass) {
|
||||
`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()),
|
||||
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(
|
||||
', ',
|
||||
)}`,
|
||||
', '
|
||||
)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ function transformExample(inExample, index, ServiceClass) {
|
||||
{},
|
||||
staticPreview,
|
||||
ServiceClass.defaultBadgeData,
|
||||
ServiceClass,
|
||||
ServiceClass
|
||||
)
|
||||
|
||||
const category = categories.find(c => c.id === ServiceClass.category)
|
||||
@@ -135,7 +135,7 @@ function transformExample(inExample, index, ServiceClass) {
|
||||
example: {
|
||||
pattern: makeFullUrl(
|
||||
ServiceClass.route.base,
|
||||
pattern || ServiceClass.route.pattern,
|
||||
pattern || ServiceClass.route.pattern
|
||||
),
|
||||
namedParams,
|
||||
queryParams,
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('validateExample function', function () {
|
||||
|
||||
validExamples.forEach(example => {
|
||||
expect(() =>
|
||||
validateExample(example, 0, { route: {}, name: 'mockService' }),
|
||||
validateExample(example, 0, { route: {}, name: 'mockService' })
|
||||
).not.to.throw(Error)
|
||||
})
|
||||
})
|
||||
@@ -66,7 +66,7 @@ describe('validateExample function', function () {
|
||||
|
||||
invalidExamples.forEach(example => {
|
||||
expect(() =>
|
||||
validateExample(example, 0, { route: {}, name: 'mockService' }),
|
||||
validateExample(example, 0, { route: {}, name: 'mockService' })
|
||||
).to.throw(Error)
|
||||
})
|
||||
})
|
||||
@@ -93,7 +93,7 @@ test(transformExample, function () {
|
||||
keywords: ['hello'],
|
||||
},
|
||||
0,
|
||||
ExampleService,
|
||||
ExampleService
|
||||
).expect({
|
||||
title: 'ExampleService',
|
||||
example: {
|
||||
@@ -119,7 +119,7 @@ test(transformExample, function () {
|
||||
keywords: ['hello'],
|
||||
},
|
||||
0,
|
||||
ExampleService,
|
||||
ExampleService
|
||||
).expect({
|
||||
title: 'ExampleService',
|
||||
example: {
|
||||
@@ -146,7 +146,7 @@ test(transformExample, function () {
|
||||
keywords: ['hello'],
|
||||
},
|
||||
0,
|
||||
ExampleService,
|
||||
ExampleService
|
||||
).expect({
|
||||
title: 'ExampleService',
|
||||
example: {
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('got wrapper', function () {
|
||||
.reply(200, 'x'.repeat(101))
|
||||
const sendRequest = _fetchFactory(100)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar'),
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(InvalidResponse, 'Maximum response size exceeded')
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('got wrapper', function () {
|
||||
nock('https://www.google.com').get('/foo/bar').replyWithError('oh no')
|
||||
const sendRequest = _fetchFactory(1024)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar'),
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(Inaccessible, 'oh no')
|
||||
})
|
||||
|
||||
@@ -38,10 +38,10 @@ describe('got wrapper', function () {
|
||||
nock.disableNetConnect()
|
||||
const sendRequest = _fetchFactory(1024)
|
||||
return expect(
|
||||
sendRequest('https://www.google.com/foo/bar'),
|
||||
sendRequest('https://www.google.com/foo/bar')
|
||||
).to.be.rejectedWith(
|
||||
Inaccessible,
|
||||
'Nock: Disallowed net connect for "www.google.com:443/foo/bar"',
|
||||
'Nock: Disallowed net connect for "www.google.com:443/foo/bar"'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -57,18 +57,18 @@ describe('got wrapper', function () {
|
||||
prettyMessage: 'Oh no! A terrible thing has happened',
|
||||
cacheSeconds: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
.to.be.rejectedWith(
|
||||
Inaccessible,
|
||||
"Inaccessible: Timeout awaiting 'request' for 1ms",
|
||||
"Inaccessible: Timeout awaiting 'request' for 1ms"
|
||||
)
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
.then(error => {
|
||||
expect(error).to.have.property(
|
||||
'prettyMessage',
|
||||
'Oh no! A terrible thing has happened',
|
||||
'Oh no! A terrible thing has happened'
|
||||
)
|
||||
expect(error).to.have.property('cacheSeconds', 10)
|
||||
})
|
||||
|
||||
@@ -9,16 +9,18 @@ describe('mergeQueries function', function () {
|
||||
it('merges valid gql queries', function () {
|
||||
expect(
|
||||
print(
|
||||
mergeQueries(gql`
|
||||
query ($param: String!) {
|
||||
foo(param: $param) {
|
||||
bar
|
||||
mergeQueries(
|
||||
gql`
|
||||
query ($param: String!) {
|
||||
foo(param: $param) {
|
||||
bar
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
),
|
||||
`
|
||||
)
|
||||
)
|
||||
).to.equalIgnoreSpaces(
|
||||
'query ($param: String!) { foo(param: $param) { bar } }',
|
||||
'query ($param: String!) { foo(param: $param) { bar } }'
|
||||
)
|
||||
|
||||
expect(
|
||||
@@ -35,11 +37,11 @@ describe('mergeQueries function', function () {
|
||||
query {
|
||||
baz
|
||||
}
|
||||
`,
|
||||
),
|
||||
),
|
||||
`
|
||||
)
|
||||
)
|
||||
).to.equalIgnoreSpaces(
|
||||
'query ($param: String!) { foo(param: $param) { bar } baz }',
|
||||
'query ($param: String!) { foo(param: $param) { bar } baz }'
|
||||
)
|
||||
|
||||
expect(
|
||||
@@ -59,9 +61,9 @@ describe('mergeQueries function', function () {
|
||||
query {
|
||||
baz
|
||||
}
|
||||
`,
|
||||
),
|
||||
),
|
||||
`
|
||||
)
|
||||
)
|
||||
).to.equalIgnoreSpaces('{ foo bar baz }')
|
||||
|
||||
expect(
|
||||
@@ -76,9 +78,9 @@ describe('mergeQueries function', function () {
|
||||
{
|
||||
bar
|
||||
}
|
||||
`,
|
||||
),
|
||||
),
|
||||
`
|
||||
)
|
||||
)
|
||||
).to.equalIgnoreSpaces('{ foo bar }')
|
||||
})
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
Deprecated,
|
||||
ImproperlyConfigured,
|
||||
} from './errors.js'
|
||||
import { pathParam, pathParams, queryParam, queryParams } from './openapi.js'
|
||||
|
||||
export {
|
||||
BaseService,
|
||||
@@ -33,8 +32,4 @@ export {
|
||||
InvalidParameter,
|
||||
ImproperlyConfigured,
|
||||
Deprecated,
|
||||
pathParam,
|
||||
pathParams,
|
||||
queryParam,
|
||||
queryParams,
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
const badgeData = coalesceBadge(
|
||||
filteredQueryParams,
|
||||
{ label: 'vendor', message: 'unresponsive' },
|
||||
{},
|
||||
{}
|
||||
)
|
||||
const svg = makeBadge(badgeData)
|
||||
const extension = (match.slice(-1)[0] || '.svg').replace(/^\./, '')
|
||||
@@ -126,7 +126,7 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
|
||||
const svg = makeBadge(badgeData)
|
||||
setCacheHeadersOnResponse(ask.res, badgeData.cacheLengthSeconds)
|
||||
makeSend(format, ask.res, end)(svg)
|
||||
},
|
||||
}
|
||||
)
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
if (result && result.catch) {
|
||||
|
||||
@@ -18,7 +18,7 @@ function fakeHandler(queryParams, match, sendBadge, request) {
|
||||
label: 'testing',
|
||||
message: someValue,
|
||||
},
|
||||
{},
|
||||
{}
|
||||
)
|
||||
sendBadge(format, badgeData)
|
||||
}
|
||||
@@ -35,7 +35,7 @@ function createFakeHandlerWithCacheLength(cacheLengthSeconds) {
|
||||
{},
|
||||
{
|
||||
_cacheLength: cacheLengthSeconds,
|
||||
},
|
||||
}
|
||||
)
|
||||
sendBadge(format, badgeData)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ describe('The request handler', function () {
|
||||
beforeEach(function () {
|
||||
camp.route(
|
||||
/^\/testing\/([^/]+)\.(svg|png|gif|jpg|json)$/,
|
||||
handleRequest(standardCacheHeaders, { handler: fakeHandler }),
|
||||
handleRequest(standardCacheHeaders, { handler: fakeHandler })
|
||||
)
|
||||
})
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('The request handler', function () {
|
||||
beforeEach(function () {
|
||||
camp.route(
|
||||
/^\/testing\/([^/]+)\.(svg|png|gif|jpg|json)$/,
|
||||
handleRequest(standardCacheHeaders, fakeHandler),
|
||||
handleRequest(standardCacheHeaders, fakeHandler)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -119,8 +119,8 @@ describe('The request handler', function () {
|
||||
cacheHeaderConfig,
|
||||
(queryParams, match, sendBadge, request) => {
|
||||
fakeHandler(queryParams, match, sendBadge, request)
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ describe('The request handler', function () {
|
||||
register({ cacheHeaderConfig: { defaultCacheLengthSeconds: 900 } })
|
||||
const { headers } = await got(`${baseUrl}/testing/123.json`)
|
||||
const expectedExpiry = new Date(
|
||||
+new Date(headers.date) + 900000,
|
||||
+new Date(headers.date) + 900000
|
||||
).toGMTString()
|
||||
expect(headers.expires).to.equal(expectedExpiry)
|
||||
expect(headers['cache-control']).to.equal('max-age=900, s-maxage=900')
|
||||
@@ -142,7 +142,7 @@ describe('The request handler', function () {
|
||||
|
||||
const { headers } = await got(`${baseUrl}/testing/123.json`)
|
||||
const expectedExpiry = new Date(
|
||||
+new Date(headers.date) + 900000,
|
||||
+new Date(headers.date) + 900000
|
||||
).toGMTString()
|
||||
expect(headers.expires).to.equal(expectedExpiry)
|
||||
expect(headers['cache-control']).to.equal('max-age=900, s-maxage=900')
|
||||
@@ -158,10 +158,10 @@ describe('The request handler', function () {
|
||||
queryParams,
|
||||
match,
|
||||
sendBadge,
|
||||
request,
|
||||
request
|
||||
)
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const { headers } = await got(`${baseUrl}/testing/123.json`)
|
||||
@@ -178,10 +178,10 @@ describe('The request handler', function () {
|
||||
queryParams,
|
||||
match,
|
||||
sendBadge,
|
||||
request,
|
||||
request
|
||||
)
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const { headers } = await got(`${baseUrl}/testing/123.json`)
|
||||
@@ -191,10 +191,10 @@ describe('The request handler', function () {
|
||||
it('should set the expires header to current time + cacheSeconds', async function () {
|
||||
register({ cacheHeaderConfig: { defaultCacheLengthSeconds: 0 } })
|
||||
const { headers } = await got(
|
||||
`${baseUrl}/testing/123.json?cacheSeconds=3600`,
|
||||
`${baseUrl}/testing/123.json?cacheSeconds=3600`
|
||||
)
|
||||
const expectedExpiry = new Date(
|
||||
+new Date(headers.date) + 3600000,
|
||||
+new Date(headers.date) + 3600000
|
||||
).toGMTString()
|
||||
expect(headers.expires).to.equal(expectedExpiry)
|
||||
expect(headers['cache-control']).to.equal('max-age=3600, s-maxage=3600')
|
||||
@@ -203,10 +203,10 @@ describe('The request handler', function () {
|
||||
it('should ignore cacheSeconds when shorter than defaultCacheLengthSeconds', async function () {
|
||||
register({ cacheHeaderConfig: { defaultCacheLengthSeconds: 600 } })
|
||||
const { headers } = await got(
|
||||
`${baseUrl}/testing/123.json?cacheSeconds=300`,
|
||||
`${baseUrl}/testing/123.json?cacheSeconds=300`
|
||||
)
|
||||
const expectedExpiry = new Date(
|
||||
+new Date(headers.date) + 600000,
|
||||
+new Date(headers.date) + 600000
|
||||
).toGMTString()
|
||||
expect(headers.expires).to.equal(expectedExpiry)
|
||||
expect(headers['cache-control']).to.equal('max-age=600, s-maxage=600')
|
||||
@@ -217,7 +217,7 @@ describe('The request handler', function () {
|
||||
const { headers } = await got(`${baseUrl}/testing/123.json`)
|
||||
expect(headers.expires).to.equal(headers.date)
|
||||
expect(headers['cache-control']).to.equal(
|
||||
'no-cache, no-store, must-revalidate',
|
||||
'no-cache, no-store, must-revalidate'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -234,7 +234,7 @@ describe('The request handler', function () {
|
||||
++handlerCallCount
|
||||
fakeHandler(queryParams, match, sendBadge, request)
|
||||
},
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
@@ -242,7 +242,7 @@ describe('The request handler', function () {
|
||||
await performTwoRequests(
|
||||
baseUrl,
|
||||
'/testing/123.svg?foo=1',
|
||||
'/testing/123.svg?foo=2',
|
||||
'/testing/123.svg?foo=2'
|
||||
)
|
||||
expect(handlerCallCount).to.equal(2)
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ const serviceDir = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'..',
|
||||
'services',
|
||||
'services'
|
||||
)
|
||||
|
||||
function toUnixPath(path) {
|
||||
@@ -51,14 +51,14 @@ async function loadServiceClasses(servicePaths) {
|
||||
const serviceClasses = []
|
||||
for await (const servicePath of servicePaths) {
|
||||
const currentServiceClasses = Object.values(
|
||||
await import(`file://${servicePath}`),
|
||||
await import(`file://${servicePath}`)
|
||||
).flatMap(element =>
|
||||
typeof element === 'object' ? Object.values(element) : element,
|
||||
typeof element === 'object' ? Object.values(element) : element
|
||||
)
|
||||
|
||||
if (currentServiceClasses.length === 0) {
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services`,
|
||||
`Expected ${servicePath} to export a service or a collection of services`
|
||||
)
|
||||
}
|
||||
currentServiceClasses.forEach(serviceClass => {
|
||||
@@ -71,7 +71,7 @@ async function loadServiceClasses(servicePaths) {
|
||||
return serviceClasses.push(serviceClass)
|
||||
}
|
||||
throw new InvalidService(
|
||||
`Expected ${servicePath} to export a service or a collection of services; one of them was ${serviceClass}`,
|
||||
`Expected ${servicePath} to export a service or a collection of services; one of them was ${serviceClass}`
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -80,20 +80,8 @@ async function loadServiceClasses(servicePaths) {
|
||||
serviceClasses.map(({ name }) => name),
|
||||
{
|
||||
message: 'Duplicate service names found',
|
||||
},
|
||||
)
|
||||
|
||||
const routeSummaries = []
|
||||
serviceClasses.forEach(function (serviceClass) {
|
||||
if (serviceClass.openApi) {
|
||||
for (const route of Object.values(serviceClass.openApi)) {
|
||||
routeSummaries.push(route.get.summary)
|
||||
}
|
||||
}
|
||||
})
|
||||
assertNamesUnique(routeSummaries, {
|
||||
message: 'Duplicate route summary found',
|
||||
})
|
||||
)
|
||||
|
||||
return serviceClasses
|
||||
}
|
||||
@@ -114,8 +102,8 @@ async function collectDefinitions() {
|
||||
async function loadTesters() {
|
||||
return Promise.all(
|
||||
getServicePaths('*.tester.js').map(
|
||||
async path => await import(`file://${path}`),
|
||||
),
|
||||
async path => await import(`file://${path}`)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,56 +12,50 @@ chai.use(chaiAsPromised)
|
||||
const { expect } = chai
|
||||
const fixturesDir = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'loader-test-fixtures',
|
||||
'loader-test-fixtures'
|
||||
)
|
||||
|
||||
describe('loadServiceClasses function', function () {
|
||||
it('throws if module exports empty', async function () {
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'empty-undefined.fixture.js'),
|
||||
]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-undefined.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-array.fixture.js')]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-array.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-object.fixture.js')]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-object.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'empty-no-export.fixture.js'),
|
||||
]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'empty-no-export.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
path.join(fixturesDir, 'empty-array.fixture.js'),
|
||||
]),
|
||||
])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
})
|
||||
|
||||
it('throws if module exports invalid', async function () {
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'invalid-no-base.fixture.js'),
|
||||
]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'invalid-no-base.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'invalid-wrong-base.fixture.js'),
|
||||
]),
|
||||
])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([path.join(fixturesDir, 'invalid-mixed.fixture.js')]),
|
||||
loadServiceClasses([path.join(fixturesDir, 'invalid-mixed.fixture.js')])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
await expect(
|
||||
loadServiceClasses([
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
path.join(fixturesDir, 'invalid-no-base.fixture.js'),
|
||||
]),
|
||||
])
|
||||
).to.be.rejectedWith(InvalidService)
|
||||
})
|
||||
|
||||
@@ -71,7 +65,7 @@ describe('loadServiceClasses function', function () {
|
||||
path.join(fixturesDir, 'valid-array.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-object.fixture.js'),
|
||||
path.join(fixturesDir, 'valid-class.fixture.js'),
|
||||
]),
|
||||
])
|
||||
).to.eventually.have.length(5)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
/**
|
||||
* Functions for publishing the shields.io URL schema as an OpenAPI Document
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
const baseUrl = process.env.BASE_URL
|
||||
const globalParamRefs = [
|
||||
{ $ref: '#/components/parameters/style' },
|
||||
@@ -164,7 +158,7 @@ function examples2openapi(examples) {
|
||||
const parameters = [
|
||||
...pathParams,
|
||||
...Object.entries(queryParams).map(([paramName, exampleValue]) =>
|
||||
param2openapi(pattern, paramName, exampleValue, 'query'),
|
||||
param2openapi(pattern, paramName, exampleValue, 'query')
|
||||
),
|
||||
...globalParamRefs,
|
||||
]
|
||||
@@ -204,7 +198,7 @@ function services2openapi(services) {
|
||||
if (service.openApi) {
|
||||
// if the service declares its own OpenAPI definition, use that...
|
||||
for (const [key, value] of Object.entries(
|
||||
addGlobalProperties(service.openApi),
|
||||
addGlobalProperties(service.openApi)
|
||||
)) {
|
||||
if (key in paths) {
|
||||
throw new Error(`Conflicting route: ${key}`)
|
||||
@@ -214,7 +208,7 @@ function services2openapi(services) {
|
||||
} else {
|
||||
// ...otherwise do our best to build one from examples[]
|
||||
for (const [key, value] of Object.entries(
|
||||
examples2openapi(service.examples),
|
||||
examples2openapi(service.examples)
|
||||
)) {
|
||||
// allow conflicting routes for legacy examples
|
||||
paths[key] = value
|
||||
@@ -253,7 +247,7 @@ function category2openapi(category, services) {
|
||||
in: 'query',
|
||||
required: false,
|
||||
description:
|
||||
'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository. <a href="/docs/logos">Further info</a>.',
|
||||
'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository.',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
@@ -338,132 +332,4 @@ function category2openapi(category, services) {
|
||||
return spec
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an OpenAPI path parameter object
|
||||
*
|
||||
* @param {module:core/base-service/openapi~PathParamInput} param Input param
|
||||
* @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
|
||||
* @see https://swagger.io/specification/#parameter-object
|
||||
*/
|
||||
function pathParam({
|
||||
name,
|
||||
example,
|
||||
schema = { type: 'string' },
|
||||
description,
|
||||
}) {
|
||||
return { name, in: 'path', required: true, schema, example, description }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an array of OpenAPI path parameter objects
|
||||
* The code
|
||||
* ```
|
||||
* const params = pathParams(
|
||||
* { name: 'name1', example: 'example1' },
|
||||
* { name: 'name2', example: 'example2' },
|
||||
* )
|
||||
* ```
|
||||
* is equivilent to
|
||||
* ```
|
||||
* const params = [
|
||||
* pathParam({ name: 'name1', example: 'example1' }),
|
||||
* pathParam({ name: 'name2', example: 'example2' }),
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param {...module:core/base-service/openapi~PathParamInput} params Input params
|
||||
* @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
|
||||
* @see {@link module:core/base-service/openapi~pathParam}
|
||||
*/
|
||||
function pathParams(...params) {
|
||||
return params.map(param => pathParam(param))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an OpenAPI query parameter object
|
||||
*
|
||||
* @param {module:core/base-service/openapi~QueryParamInput} param Input param
|
||||
* @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
|
||||
* @see https://swagger.io/specification/#parameter-object
|
||||
*/
|
||||
function queryParam({
|
||||
name,
|
||||
example,
|
||||
schema = { type: 'string' },
|
||||
required = false,
|
||||
description,
|
||||
}) {
|
||||
const param = { name, in: 'query', required, schema, example, description }
|
||||
if (example === null && schema.type === 'boolean') {
|
||||
param.allowEmptyValue = true
|
||||
}
|
||||
return param
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an array of OpenAPI query parameter objects
|
||||
* The code
|
||||
* ```
|
||||
* const params = queryParams(
|
||||
* { name: 'name1', example: 'example1' },
|
||||
* { name: 'name2', example: 'example2' },
|
||||
* )
|
||||
* ```
|
||||
* is equivilent to
|
||||
* ```
|
||||
* const params = [
|
||||
* queryParam({ name: 'name1', example: 'example1' }),
|
||||
* queryParams({ name: 'name2', example: 'example2' }),
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param {...module:core/base-service/openapi~QueryParamInput} params Input params
|
||||
* @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
|
||||
* @see {@link module:core/base-service/openapi~queryParam}
|
||||
*/
|
||||
function queryParams(...params) {
|
||||
return params.map(param => queryParam(param))
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} PathParamInput
|
||||
* @property {string} name The name of the parameter. Parameter names are case sensitive
|
||||
* @property {string} example Example of a valid value for this parameter
|
||||
* @property {object} [schema={ type: 'string' }] Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type.
|
||||
* Normally this should be omitted as all path parameters are strings.
|
||||
* Use this when we also want to pass an enum of valid parameters
|
||||
* to be presented as a drop-down in the frontend. e.g:
|
||||
* `{'type': 'string', 'enum': ['github', 'bitbucket'}` (Optional)
|
||||
* @property {string} description A brief description of the parameter (Optional)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} QueryParamInput
|
||||
* @property {string} name The name of the parameter. Parameter names are case sensitive
|
||||
* @property {string|null} example Example of a valid value for this parameter
|
||||
* @property {object} [schema={ type: 'string' }] Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type. This can normally be omitted.
|
||||
* Query params are usually strings. (Optional)
|
||||
* @property {boolean} [required=false] Determines whether this parameter is mandatory (Optional)
|
||||
* @property {string} description A brief description of the parameter (Optional)
|
||||
*/
|
||||
|
||||
/**
|
||||
* OpenAPI Parameter Object
|
||||
*
|
||||
* @typedef {object} OpenApiParam
|
||||
* @property {string} name The name of the parameter
|
||||
* @property {string|null} example Example of a valid value for this parameter
|
||||
* @property {('path'|'query')} in The location of the parameter
|
||||
* @property {object} schema Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type.
|
||||
* @property {boolean} required Determines whether this parameter is mandatory
|
||||
* @property {string} description A brief description of the parameter
|
||||
* @property {boolean} allowEmptyValue If true, allows the ability to pass an empty value to this parameter
|
||||
*/
|
||||
|
||||
export { category2openapi, pathParam, pathParams, queryParam, queryParams }
|
||||
export { category2openapi }
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import chai from 'chai'
|
||||
import {
|
||||
category2openapi,
|
||||
pathParam,
|
||||
pathParams,
|
||||
queryParam,
|
||||
queryParams,
|
||||
} from './openapi.js'
|
||||
import { category2openapi } from './openapi.js'
|
||||
import BaseJsonService from './base-json.js'
|
||||
const { expect } = chai
|
||||
|
||||
@@ -98,7 +92,7 @@ const expected = {
|
||||
in: 'query',
|
||||
required: false,
|
||||
description:
|
||||
'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository. <a href="/docs/logos">Further info</a>.',
|
||||
'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository.',
|
||||
schema: { type: 'string' },
|
||||
example: 'appveyor',
|
||||
},
|
||||
@@ -377,153 +371,8 @@ describe('category2openapi', function () {
|
||||
category2openapi({ name: 'build' }, [
|
||||
OpenApiService.getDefinition(),
|
||||
LegacyService.getDefinition(),
|
||||
]),
|
||||
),
|
||||
])
|
||||
)
|
||||
).to.deep.equal(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pathParam, pathParams', function () {
|
||||
it('generates a pathParam with defaults', function () {
|
||||
const input = { name: 'name', example: 'example' }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example',
|
||||
description: undefined,
|
||||
}
|
||||
expect(pathParam(input)).to.deep.equal(expected)
|
||||
expect(pathParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates a pathParam with custom args', function () {
|
||||
const input = {
|
||||
name: 'name',
|
||||
example: true,
|
||||
schema: { type: 'boolean' },
|
||||
description: 'long desc',
|
||||
}
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'boolean',
|
||||
},
|
||||
example: true,
|
||||
description: 'long desc',
|
||||
}
|
||||
expect(pathParam(input)).to.deep.equal(expected)
|
||||
expect(pathParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates multiple pathParams', function () {
|
||||
expect(
|
||||
pathParams(
|
||||
{ name: 'name1', example: 'example1' },
|
||||
{ name: 'name2', example: 'example2' },
|
||||
),
|
||||
).to.deep.equal([
|
||||
{
|
||||
name: 'name1',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example1',
|
||||
description: undefined,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example2',
|
||||
description: undefined,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('queryParam, queryParams', function () {
|
||||
it('generates a queryParam with defaults', function () {
|
||||
const input = { name: 'name', example: 'example' }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example',
|
||||
description: undefined,
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates queryParam with custom args', function () {
|
||||
const input = {
|
||||
name: 'name',
|
||||
example: 'example',
|
||||
required: true,
|
||||
description: 'long desc',
|
||||
}
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'example',
|
||||
description: 'long desc',
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates a queryParam with boolean/null example', function () {
|
||||
const input = { name: 'name', example: null, schema: { type: 'boolean' } }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'boolean' },
|
||||
allowEmptyValue: true,
|
||||
example: null,
|
||||
description: undefined,
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates multiple queryParams', function () {
|
||||
expect(
|
||||
queryParams(
|
||||
{ name: 'name1', example: 'example1' },
|
||||
{ name: 'name2', example: 'example2' },
|
||||
),
|
||||
).to.deep.equal([
|
||||
{
|
||||
name: 'name1',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example1',
|
||||
description: undefined,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example2',
|
||||
description: undefined,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@ const attrSchema = Joi.object({
|
||||
.required()
|
||||
.error(
|
||||
() =>
|
||||
'"transformPath" must be a function that transforms named params to a new path',
|
||||
'"transformPath" must be a function that transforms named params to a new path'
|
||||
),
|
||||
transformQueryParams: Joi.func().arity(1),
|
||||
dateAdded: Joi.date().required(),
|
||||
@@ -80,7 +80,7 @@ export default function redirector(attrs) {
|
||||
'inbound',
|
||||
emojic.arrowHeadingUp,
|
||||
'Redirector',
|
||||
route.base,
|
||||
route.base
|
||||
)
|
||||
trace.logTrace('inbound', emojic.ticket, 'Named params', namedParams)
|
||||
trace.logTrace('inbound', emojic.crayon, 'Query params', queryParams)
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('Redirector', function () {
|
||||
redirector({
|
||||
...attrs,
|
||||
name: 'ShinyRedirect',
|
||||
}).name,
|
||||
}).name
|
||||
).to.equal('ShinyRedirect')
|
||||
})
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('Redirector', function () {
|
||||
|
||||
it('throws the expected error when dateAdded is missing', function () {
|
||||
expect(() =>
|
||||
redirector({ route, category, transformPath }).validateDefinition(),
|
||||
redirector({ route, category, transformPath }).validateDefinition()
|
||||
).to.throw('"dateAdded" is required')
|
||||
})
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('Redirector', function () {
|
||||
})
|
||||
ServiceClass.register(
|
||||
{ camp },
|
||||
{ rasterUrl: 'http://raster.example.test' },
|
||||
{ rasterUrl: 'http://raster.example.test' }
|
||||
)
|
||||
})
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/very/old/service/hello-world.svg`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
@@ -114,12 +114,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/very/old/service/hello-world.png`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'http://raster.example.test/new/service/hello-world.png',
|
||||
'http://raster.example.test/new/service/hello-world.png'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -128,12 +128,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/very/old/service/hello-world.svg?color=123&style=flat-square`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square',
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -142,12 +142,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/very/old/service/hello%0Dworld.svg?foobar=a%0Db`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello%0Dworld.svg?foobar=a%0Db',
|
||||
'/new/service/hello%0Dworld.svg?foobar=a%0Db'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -174,12 +174,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/another/old/service/token/abc123/hello-world.svg`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello-world.svg?token=abc123',
|
||||
'/new/service/hello-world.svg?token=abc123'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -188,12 +188,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/another/old/service/token/abc123/hello-world.svg?color=123&style=flat-square`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square&token=abc123',
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square&token=abc123'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -202,12 +202,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/another/old/service/token/abc123/hello-world.svg?color=123&style=flat-square&token=def456`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square&token=abc123',
|
||||
'/new/service/hello-world.svg?color=123&style=flat-square&token=abc123'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -229,12 +229,12 @@ describe('Redirector', function () {
|
||||
`${baseUrl}/override/service/token/abc123/hello-world.svg?style=flat-square&token=def456`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'/new/service/hello-world.svg?style=flat-square&token=def456',
|
||||
'/new/service/hello-world.svg?style=flat-square&token=def456'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,7 +40,7 @@ async function getCachedResource({
|
||||
}
|
||||
|
||||
const { buffer } = await checkErrorResponse({})(
|
||||
await requestFetcher(url, options),
|
||||
await requestFetcher(url, options)
|
||||
)
|
||||
|
||||
let reqData
|
||||
|
||||
@@ -52,7 +52,7 @@ function namedParamsForMatch(captureNames = [], match, ServiceClass) {
|
||||
if (captureNames.length !== captures.length) {
|
||||
throw new Error(
|
||||
`Service ${ServiceClass.name} declares incorrect number of named params ` +
|
||||
`(expected ${captures.length}, got ${captureNames.length})`,
|
||||
`(expected ${captures.length}, got ${captureNames.length})`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +86,9 @@ describe('Route helpers', function () {
|
||||
expect(() =>
|
||||
namedParamsForMatch(captureNames, regex.exec('/foo/bar/baz.svg'), {
|
||||
name: 'MyService',
|
||||
}),
|
||||
})
|
||||
).to.throw(
|
||||
'Service MyService declares incorrect number of named params (expected 2, got 1)',
|
||||
'Service MyService declares incorrect number of named params (expected 2, got 1)'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -96,14 +96,14 @@ describe('Route helpers', function () {
|
||||
expect(
|
||||
getQueryParamNames({
|
||||
queryParamSchema: Joi.object({ foo: Joi.string() }).required(),
|
||||
}),
|
||||
})
|
||||
).to.deep.equal(['foo'])
|
||||
expect(
|
||||
getQueryParamNames({
|
||||
queryParamSchema: Joi.object({ foo: Joi.string() })
|
||||
.rename('bar', 'foo', { ignoreUndefined: true, override: true })
|
||||
.required(),
|
||||
}),
|
||||
})
|
||||
).to.deep.equal(['foo', 'bar'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ const serviceDefinition = Joi.object({
|
||||
Joi.object({
|
||||
format: Joi.string().required(),
|
||||
queryParams: arrayOfStrings,
|
||||
}),
|
||||
})
|
||||
),
|
||||
examples: Joi.array()
|
||||
.items(
|
||||
@@ -40,7 +40,7 @@ const serviceDefinition = Joi.object({
|
||||
documentation: Joi.object({
|
||||
__html: Joi.string().required(), // Valid HTML.
|
||||
}),
|
||||
}),
|
||||
})
|
||||
)
|
||||
.default([]),
|
||||
openApi: Joi.object().pattern(
|
||||
@@ -48,7 +48,7 @@ const serviceDefinition = Joi.object({
|
||||
Joi.object({
|
||||
get: Joi.object({
|
||||
summary: Joi.string().required(),
|
||||
description: Joi.string(),
|
||||
description: Joi.string().required(),
|
||||
parameters: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
@@ -56,23 +56,19 @@ const serviceDefinition = Joi.object({
|
||||
description: Joi.string(),
|
||||
in: Joi.string().valid('query', 'path').required(),
|
||||
required: Joi.boolean().required(),
|
||||
schema: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
enum: Joi.array(),
|
||||
}).required(),
|
||||
allowEmptyValue: Joi.boolean(),
|
||||
example: Joi.string().allow(null),
|
||||
}),
|
||||
schema: Joi.object({ type: Joi.string().required() }).required(),
|
||||
example: Joi.string(),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.required(),
|
||||
}).required(),
|
||||
}).required(),
|
||||
}).required()
|
||||
),
|
||||
}).required()
|
||||
|
||||
function assertValidServiceDefinition(service, message = undefined) {
|
||||
Joi.assert(service, serviceDefinition, message)
|
||||
function assertValidServiceDefinition(example, message = undefined) {
|
||||
Joi.assert(example, serviceDefinition, message)
|
||||
}
|
||||
|
||||
const serviceDefinitionExport = Joi.object({
|
||||
@@ -83,7 +79,7 @@ const serviceDefinitionExport = Joi.object({
|
||||
id: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
keywords: arrayOfStrings,
|
||||
}),
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
services: Joi.array().items(serviceDefinition).required(),
|
||||
|
||||
@@ -12,7 +12,7 @@ function validate(
|
||||
allowAndStripUnknownKeys = true,
|
||||
},
|
||||
data,
|
||||
schema,
|
||||
schema
|
||||
) {
|
||||
if (!schema || !Joi.isSchema(schema)) {
|
||||
throw Error('A Joi schema is required')
|
||||
@@ -28,7 +28,7 @@ function validate(
|
||||
'validate',
|
||||
emojic.womanShrugging,
|
||||
traceErrorMessage,
|
||||
error.message,
|
||||
error.message
|
||||
)
|
||||
|
||||
let prettyMessage = prettyErrorMessage
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('validate', function () {
|
||||
sinon.match.string,
|
||||
traceSuccessMessage,
|
||||
{ requiredString: 'bar' },
|
||||
{ deep: true },
|
||||
{ deep: true }
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -60,13 +60,13 @@ describe('validate', function () {
|
||||
validate(
|
||||
options,
|
||||
{ requiredString: ['this', "shouldn't", 'work'] },
|
||||
schema,
|
||||
schema
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidParameter)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Parameter: "requiredString" must be a string',
|
||||
'Invalid Parameter: "requiredString" must be a string'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal(prettyErrorMessage)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ describe('validate', function () {
|
||||
'validate',
|
||||
sinon.match.string,
|
||||
traceErrorMessage,
|
||||
'"requiredString" must be a string',
|
||||
'"requiredString" must be a string'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -86,16 +86,16 @@ describe('validate', function () {
|
||||
{
|
||||
requiredString: ['this', "shouldn't", 'work'],
|
||||
},
|
||||
schema,
|
||||
schema
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidParameter)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Parameter: "requiredString" must be a string',
|
||||
'Invalid Parameter: "requiredString" must be a string'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal(
|
||||
`${prettyErrorMessage}: requiredString`,
|
||||
`${prettyErrorMessage}: requiredString`
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -107,13 +107,13 @@ describe('validate', function () {
|
||||
validate(
|
||||
{ ...options, allowAndStripUnknownKeys: false, includeKeys: true },
|
||||
{ requiredString: 'bar', extra: 'nonsense', more: 'bogus' },
|
||||
schema,
|
||||
schema
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidParameter)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Parameter: "extra" is not allowed. "more" is not allowed',
|
||||
'Invalid Parameter: "extra" is not allowed. "more" is not allowed'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal(`${prettyErrorMessage}: extra, more`)
|
||||
}
|
||||
|
||||
@@ -27,16 +27,14 @@ export default class InfluxMetrics {
|
||||
response = await got.post(request)
|
||||
} catch (error) {
|
||||
log.error(
|
||||
new Error(
|
||||
`Cannot push metrics. Cause: ${error.name}: ${error.message}`,
|
||||
),
|
||||
new Error(`Cannot push metrics. Cause: ${error.name}: ${error.message}`)
|
||||
)
|
||||
}
|
||||
if (response && response.statusCode >= 300) {
|
||||
log.error(
|
||||
new Error(
|
||||
`Cannot push metrics. ${request.url} responded with status code ${response.statusCode}`,
|
||||
),
|
||||
`Cannot push metrics. ${request.url} responded with status code ${response.statusCode}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -44,7 +42,7 @@ export default class InfluxMetrics {
|
||||
startPushingMetrics() {
|
||||
this._intervalId = setInterval(
|
||||
() => this.sendMetrics(),
|
||||
this._config.intervalSeconds * 1000,
|
||||
this._config.intervalSeconds * 1000
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('Influx metrics', function () {
|
||||
const influxMetrics = new InfluxMetrics(metricInstance, customConfig)
|
||||
|
||||
expect(await influxMetrics.metrics()).to.be.contain(
|
||||
'instance=test-hostname',
|
||||
'instance=test-hostname'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('Influx metrics', function () {
|
||||
const influxMetrics = new InfluxMetrics(metricInstance, customConfig)
|
||||
|
||||
expect(await influxMetrics.metrics()).to.be.contain(
|
||||
'instance=test-hostname-alias',
|
||||
'instance=test-hostname-alias'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -96,7 +96,7 @@ describe('Influx metrics', function () {
|
||||
.persist()
|
||||
.post(
|
||||
'/metrics',
|
||||
'prometheus,application=shields,env=test-env,instance=instance2 counter1=11',
|
||||
'prometheus,application=shields,env=test-env,instance=instance2 counter1=11'
|
||||
)
|
||||
.basicAuth({ user: 'metrics-username', pass: 'metrics-password' })
|
||||
.reply(200)
|
||||
@@ -117,7 +117,7 @@ describe('Influx metrics', function () {
|
||||
await clock.tickAsync(10)
|
||||
expect(scope.isDone()).to.be.equal(
|
||||
true,
|
||||
`pending mocks: ${scope.pendingMocks()}`,
|
||||
`pending mocks: ${scope.pendingMocks()}`
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -150,9 +150,9 @@ describe('Influx metrics', function () {
|
||||
.and(
|
||||
sinon.match.has(
|
||||
'message',
|
||||
'Cannot push metrics. Cause: RequestError: Nock: Disallowed net connect for "shields-metrics.io:443/metrics"',
|
||||
),
|
||||
),
|
||||
'Cannot push metrics. Cause: RequestError: Nock: Disallowed net connect for "shields-metrics.io:443/metrics"'
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -167,9 +167,9 @@ describe('Influx metrics', function () {
|
||||
.and(
|
||||
sinon.match.has(
|
||||
'message',
|
||||
'Cannot push metrics. https://shields-metrics.io/metrics responded with status code 400',
|
||||
),
|
||||
),
|
||||
'Cannot push metrics. https://shields-metrics.io/metrics responded with status code 400'
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ function promClientJsonToInfluxV2(metrics, extraLabels = {}) {
|
||||
return metrics
|
||||
.flatMap(metric => {
|
||||
const valuesByLabels = groupBy(metric.values, value =>
|
||||
JSON.stringify(Object.entries(value.labels).sort()),
|
||||
JSON.stringify(Object.entries(value.labels).sort())
|
||||
)
|
||||
return Object.values(valuesByLabels).map(metricsWithSameLabel => {
|
||||
const labels = Object.entries(metricsWithSameLabel[0].labels)
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('Metric format converters', function () {
|
||||
prometheus,le=50 histogram1_bucket=2
|
||||
prometheus,le=15 histogram1_bucket=2
|
||||
prometheus,le=5 histogram1_bucket=1
|
||||
prometheus histogram1_count=3,histogram1_sum=111`),
|
||||
prometheus histogram1_count=3,histogram1_sum=111`)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -118,7 +118,7 @@ prometheus histogram1_count=3,histogram1_sum=111`),
|
||||
prometheus,le=50 histogram1_bucket=2
|
||||
prometheus,le=15 histogram1_bucket=2
|
||||
prometheus,le=5 histogram1_bucket=1
|
||||
prometheus histogram1_count=3,histogram1_sum=111`),
|
||||
prometheus histogram1_count=3,histogram1_sum=111`)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -145,7 +145,7 @@ prometheus histogram1_count=3,histogram1_sum=111`),
|
||||
sortLines(`prometheus,quantile=0.99 summary1=100
|
||||
prometheus,quantile=0.9 summary1=100
|
||||
prometheus,quantile=0.1 summary1=1
|
||||
prometheus summary1_count=3,summary1_sum=111`),
|
||||
prometheus summary1_count=3,summary1_sum=111`)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -167,7 +167,7 @@ prometheus summary1_count=3,summary1_sum=111`),
|
||||
sortLines(`prometheus,quantile=0.99 summary1=100
|
||||
prometheus,quantile=0.9 summary1=100
|
||||
prometheus,quantile=0.1 summary1=1
|
||||
prometheus summary1_count=3,summary1_sum=111`),
|
||||
prometheus summary1_count=3,summary1_sum=111`)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -204,7 +204,7 @@ prometheus summary1_count=3,summary1_sum=111`),
|
||||
})
|
||||
|
||||
expect(influx).to.be.equal(
|
||||
'prometheus,env=production,instance=instance1 counter1=11',
|
||||
'prometheus,env=production,instance=instance1 counter1=11'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ export default class PrometheusMetrics {
|
||||
return this.counters.serviceResponseSize.labels(
|
||||
category,
|
||||
serviceFamily,
|
||||
service,
|
||||
service
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,11 @@ const publicConfigSchema = Joi.object({
|
||||
bind: {
|
||||
port: Joi.alternatives().try(
|
||||
Joi.number().port(),
|
||||
Joi.string().pattern(/^\\\\\.\\pipe\\.+$/),
|
||||
Joi.string().pattern(/^\\\\\.\\pipe\\.+$/)
|
||||
),
|
||||
address: Joi.alternatives().try(
|
||||
Joi.string().ip().required(),
|
||||
Joi.string().hostname().required(),
|
||||
Joi.string().hostname().required()
|
||||
),
|
||||
},
|
||||
metrics: {
|
||||
@@ -154,8 +154,8 @@ const publicConfigSchema = Joi.object({
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'..',
|
||||
'public',
|
||||
),
|
||||
'public'
|
||||
)
|
||||
),
|
||||
requireCloudflare: Joi.boolean().required(),
|
||||
}).required()
|
||||
@@ -236,7 +236,7 @@ class Server {
|
||||
const publicConfig = Joi.attempt(config.public, publicConfigSchema)
|
||||
const privateConfig = this.validatePrivateConfig(
|
||||
config.private,
|
||||
privateConfigSchema,
|
||||
privateConfigSchema
|
||||
)
|
||||
// We want to require an username and a password for the influx metrics
|
||||
// only if the influx metrics are enabled. The private config schema
|
||||
@@ -245,7 +245,7 @@ class Server {
|
||||
if (publicConfig.metrics.influx && publicConfig.metrics.influx.enabled) {
|
||||
this.validatePrivateConfig(
|
||||
config.private,
|
||||
privateMetricsInfluxConfigSchema,
|
||||
privateMetricsInfluxConfigSchema
|
||||
)
|
||||
}
|
||||
this.config = {
|
||||
@@ -270,7 +270,7 @@ class Server {
|
||||
Object.assign({}, publicConfig.metrics.influx, {
|
||||
username: privateConfig.influx_username,
|
||||
password: privateConfig.influx_password,
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -283,8 +283,8 @@ class Server {
|
||||
const badPaths = e.details.map(({ path }) => path)
|
||||
throw Error(
|
||||
`Private configuration is invalid. Check these paths: ${badPaths.join(
|
||||
',',
|
||||
)}`,
|
||||
','
|
||||
)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -350,35 +350,32 @@ class Server {
|
||||
makeSend(
|
||||
'svg',
|
||||
request.res,
|
||||
end,
|
||||
end
|
||||
)(
|
||||
makeBadge({
|
||||
label: '410',
|
||||
message: `${format} no longer available`,
|
||||
color: 'lightgray',
|
||||
format: 'svg',
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
if (!rasterUrl) {
|
||||
camp.route(
|
||||
/^\/((?!img|assets\/)).*\.png$/,
|
||||
(query, match, end, request) => {
|
||||
makeSend(
|
||||
'svg',
|
||||
request.res,
|
||||
end,
|
||||
)(
|
||||
makeBadge({
|
||||
label: '404',
|
||||
message: 'raster badges not available',
|
||||
color: 'lightgray',
|
||||
format: 'svg',
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
camp.route(/^\/((?!img\/)).*\.png$/, (query, match, end, request) => {
|
||||
makeSend(
|
||||
'svg',
|
||||
request.res,
|
||||
end
|
||||
)(
|
||||
makeBadge({
|
||||
label: '404',
|
||||
message: 'raster badges not available',
|
||||
color: 'lightgray',
|
||||
format: 'svg',
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
camp.notfound(/(\.svg|\.json|)$/, (query, match, end, request) => {
|
||||
@@ -388,14 +385,14 @@ class Server {
|
||||
makeSend(
|
||||
format,
|
||||
request.res,
|
||||
end,
|
||||
end
|
||||
)(
|
||||
makeBadge({
|
||||
label: '404',
|
||||
message: 'badge not found',
|
||||
color: 'red',
|
||||
format,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -415,21 +412,18 @@ class Server {
|
||||
|
||||
if (rasterUrl) {
|
||||
// Redirect to the raster server for raster versions of modern badges.
|
||||
camp.route(
|
||||
/^\/((?!img|assets\/)).*\.png$/,
|
||||
(queryParams, match, end, ask) => {
|
||||
ask.res.statusCode = 301
|
||||
ask.res.setHeader(
|
||||
'Location',
|
||||
rasterRedirectUrl({ rasterUrl }, ask.req.url),
|
||||
)
|
||||
camp.route(/^\/((?!img\/)).*\.png$/, (queryParams, match, end, ask) => {
|
||||
ask.res.statusCode = 301
|
||||
ask.res.setHeader(
|
||||
'Location',
|
||||
rasterRedirectUrl({ rasterUrl }, ask.req.url)
|
||||
)
|
||||
|
||||
const cacheDuration = (30 * 24 * 3600) | 0 // 30 days.
|
||||
ask.res.setHeader('Cache-Control', `max-age=${cacheDuration}`)
|
||||
const cacheDuration = (30 * 24 * 3600) | 0 // 30 days.
|
||||
ask.res.setHeader('Cache-Control', `max-age=${cacheDuration}`)
|
||||
|
||||
ask.res.end()
|
||||
},
|
||||
)
|
||||
ask.res.end()
|
||||
})
|
||||
}
|
||||
|
||||
if (redirectUrl) {
|
||||
@@ -465,8 +459,8 @@ class Server {
|
||||
rasterUrl: config.public.rasterUrl,
|
||||
private: config.private,
|
||||
public: config.public,
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('The server', function () {
|
||||
public: {
|
||||
documentRoot: path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'test-public',
|
||||
'test-public'
|
||||
),
|
||||
},
|
||||
})
|
||||
@@ -66,7 +66,7 @@ describe('The server', function () {
|
||||
|
||||
it('should return cors header for the request', async function () {
|
||||
const { statusCode, headers } = await got(
|
||||
`${baseUrl}badge/foo-bar-blue.svg`,
|
||||
`${baseUrl}badge/foo-bar-blue.svg`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(headers['access-control-allow-origin']).to.equal('*')
|
||||
@@ -77,11 +77,11 @@ describe('The server', function () {
|
||||
`${baseUrl}:fruit-apple-green.png`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'http://raster.example.test/:fruit-apple-green.png',
|
||||
'http://raster.example.test/:fruit-apple-green.png'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -90,11 +90,11 @@ describe('The server', function () {
|
||||
`${baseUrl}badge/foo-bar-blue.png`,
|
||||
{
|
||||
followRedirect: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(301)
|
||||
expect(headers.location).to.equal(
|
||||
'http://raster.example.test/badge/foo-bar-blue.png',
|
||||
'http://raster.example.test/badge/foo-bar-blue.png'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -105,7 +105,7 @@ describe('The server', function () {
|
||||
|
||||
it('should produce SVG badges with expected headers', async function () {
|
||||
const { statusCode, headers } = await got(
|
||||
`${baseUrl}:fruit-apple-green.svg`,
|
||||
`${baseUrl}:fruit-apple-green.svg`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(headers['content-type']).to.equal('image/svg+xml;charset=utf-8')
|
||||
@@ -119,7 +119,7 @@ describe('The server', function () {
|
||||
|
||||
it('should produce JSON badges with expected headers', async function () {
|
||||
const { statusCode, body, headers } = await got(
|
||||
`${baseUrl}:fruit-apple-green.json`,
|
||||
`${baseUrl}:fruit-apple-green.json`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(headers['content-type']).to.equal('application/json')
|
||||
@@ -137,7 +137,7 @@ describe('The server', function () {
|
||||
// https://github.com/badges/shields/pull/1319
|
||||
it('should not crash with a numeric logo', async function () {
|
||||
const { statusCode, body } = await got(
|
||||
`${baseUrl}:fruit-apple-green.svg?logo=1`,
|
||||
`${baseUrl}:fruit-apple-green.svg?logo=1`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(body)
|
||||
@@ -148,7 +148,7 @@ describe('The server', function () {
|
||||
|
||||
it('should not crash with a numeric link', async function () {
|
||||
const { statusCode, body } = await got(
|
||||
`${baseUrl}:fruit-apple-green.svg?link=1`,
|
||||
`${baseUrl}:fruit-apple-green.svg?link=1`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(body)
|
||||
@@ -159,7 +159,7 @@ describe('The server', function () {
|
||||
|
||||
it('should not crash with a boolean link', async function () {
|
||||
const { statusCode, body } = await got(
|
||||
`${baseUrl}:fruit-apple-green.svg?link=true`,
|
||||
`${baseUrl}:fruit-apple-green.svg?link=true`
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(body)
|
||||
@@ -173,7 +173,7 @@ describe('The server', function () {
|
||||
`${baseUrl}this/is/not/a/badge.svg`,
|
||||
{
|
||||
throwHttpErrors: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(404)
|
||||
expect(body)
|
||||
@@ -187,7 +187,7 @@ describe('The server', function () {
|
||||
`${baseUrl}this/is/most/definitely/not/a/badge.js`,
|
||||
{
|
||||
throwHttpErrors: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(404)
|
||||
expect(body)
|
||||
@@ -211,7 +211,7 @@ describe('The server', function () {
|
||||
`${baseUrl}badge/foo-bar-blue.jpg`,
|
||||
{
|
||||
throwHttpErrors: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
// TODO It would be nice if this were 404 or 410.
|
||||
expect(statusCode).to.equal(200)
|
||||
@@ -236,7 +236,7 @@ describe('The server', function () {
|
||||
await server.start()
|
||||
|
||||
const { statusCode, body } = await got(
|
||||
`${server.baseUrl}badge/foo-bar-blue.svg`,
|
||||
`${server.baseUrl}badge/foo-bar-blue.svg`
|
||||
)
|
||||
|
||||
expect(statusCode).to.be.equal(200)
|
||||
@@ -365,7 +365,7 @@ describe('The server', function () {
|
||||
it('should require url when influx configuration is enabled', function () {
|
||||
delete customConfig.public.metrics.influx.url
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.url" is required',
|
||||
'"metrics.influx.url" is required'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -378,21 +378,21 @@ describe('The server', function () {
|
||||
it('should require timeoutMilliseconds when influx configuration is enabled', function () {
|
||||
delete customConfig.public.metrics.influx.timeoutMilliseconds
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.timeoutMilliseconds" is required',
|
||||
'"metrics.influx.timeoutMilliseconds" is required'
|
||||
)
|
||||
})
|
||||
|
||||
it('should require intervalSeconds when influx configuration is enabled', function () {
|
||||
delete customConfig.public.metrics.influx.intervalSeconds
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.intervalSeconds" is required',
|
||||
'"metrics.influx.intervalSeconds" is required'
|
||||
)
|
||||
})
|
||||
|
||||
it('should require instanceIdFrom when influx configuration is enabled', function () {
|
||||
delete customConfig.public.metrics.influx.instanceIdFrom
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.instanceIdFrom" is required',
|
||||
'"metrics.influx.instanceIdFrom" is required'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -400,7 +400,7 @@ describe('The server', function () {
|
||||
customConfig.public.metrics.influx.instanceIdFrom = 'env-var'
|
||||
delete customConfig.public.metrics.influx.instanceIdEnvVarName
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.instanceIdEnvVarName" is required',
|
||||
'"metrics.influx.instanceIdEnvVarName" is required'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -422,7 +422,7 @@ describe('The server', function () {
|
||||
it('should require envLabel when influx configuration is enabled', function () {
|
||||
delete customConfig.public.metrics.influx.envLabel
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'"metrics.influx.envLabel" is required',
|
||||
'"metrics.influx.envLabel" is required'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -439,14 +439,14 @@ describe('The server', function () {
|
||||
it('should require username when influx configuration is enabled', function () {
|
||||
delete customConfig.private.influx_username
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'Private configuration is invalid. Check these paths: influx_username',
|
||||
'Private configuration is invalid. Check these paths: influx_username'
|
||||
)
|
||||
})
|
||||
|
||||
it('should require password when influx configuration is enabled', function () {
|
||||
delete customConfig.private.influx_password
|
||||
expect(() => new Server(customConfig)).to.throw(
|
||||
'Private configuration is invalid. Check these paths: influx_password',
|
||||
'Private configuration is invalid. Check these paths: influx_password'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -505,7 +505,7 @@ describe('The server', function () {
|
||||
})
|
||||
.post(
|
||||
'/metrics',
|
||||
/prometheus,application=shields,category=static,env=localhost-env,family=static-badge,instance=test-instance,service=static_badge service_requests_total=1\n/,
|
||||
/prometheus,application=shields,category=static,env=localhost-env,family=static-badge,instance=test-instance,service=static_badge service_requests_total=1\n/
|
||||
)
|
||||
.basicAuth({ user: 'influx-username', pass: 'influx-password' })
|
||||
.reply(200)
|
||||
@@ -515,7 +515,7 @@ describe('The server', function () {
|
||||
|
||||
expect(scope.isDone()).to.be.equal(
|
||||
true,
|
||||
`pending mocks: ${scope.pendingMocks()}`,
|
||||
`pending mocks: ${scope.pendingMocks()}`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -121,8 +121,8 @@ if (typeof onlyServices === 'undefined' || onlyServices.includes('*****')) {
|
||||
} else {
|
||||
console.info(
|
||||
`Running tests for ${onlyServices.length} services: ${onlyServices.join(
|
||||
', ',
|
||||
)}.\n`,
|
||||
', '
|
||||
)}.\n`
|
||||
)
|
||||
runner.only(onlyServices)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ async function createServiceTester() {
|
||||
const ServiceClass = Object.values(await import(servicePath))[0]
|
||||
if (!(ServiceClass.prototype instanceof BaseService)) {
|
||||
throw Error(
|
||||
`${servicePath} does not export a single service. Invoke new ServiceTester() directly.`,
|
||||
`${servicePath} does not export a single service. Invoke new ServiceTester() directly.`
|
||||
)
|
||||
}
|
||||
return ServiceTester.forServiceClass(ServiceClass)
|
||||
|
||||
@@ -91,11 +91,11 @@ const factory = superclass =>
|
||||
Joi.attempt(
|
||||
json[name],
|
||||
Joi.string().regex(expected),
|
||||
`${name} mismatch:`,
|
||||
`${name} mismatch:`
|
||||
)
|
||||
} else {
|
||||
throw new Error(
|
||||
"'expected' must be a string, a number, a regex, an array or a Joi schema",
|
||||
"'expected' must be a string, a number, a regex, an array or a Joi schema"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class Runner {
|
||||
*/
|
||||
async prepare() {
|
||||
this.testers = (await loadTesters()).flatMap(testerModule =>
|
||||
Object.values(testerModule),
|
||||
Object.values(testerModule)
|
||||
)
|
||||
this.testers.forEach(tester => {
|
||||
tester.beforeEach = () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('Services from PR title', function () {
|
||||
])
|
||||
given('[CRAN CPAN CTAN] Add test coverage').expect(['cran', 'cpan', 'ctan'])
|
||||
given(
|
||||
'[RFC] Add Joi-based request validation to BaseJsonService and rewrite [NPM] badges',
|
||||
'[RFC] Add Joi-based request validation to BaseJsonService and rewrite [NPM] badges'
|
||||
).expect(['npm'])
|
||||
given('make changes to [CRAN] and [CPAN]').expect(['cran', 'cpan'])
|
||||
given('[github appveyor ]').expect(['github', 'appveyor'])
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('SQL token persistence', function () {
|
||||
|
||||
beforeEach('Create temporary table', async function () {
|
||||
await pool.query(
|
||||
`CREATE TEMPORARY TABLE ${tableName} (LIKE github_user_tokens INCLUDING ALL);`,
|
||||
`CREATE TEMPORARY TABLE ${tableName} (LIKE github_user_tokens INCLUDING ALL);`
|
||||
)
|
||||
})
|
||||
afterEach('Drop temporary table', async function () {
|
||||
@@ -57,7 +57,7 @@ describe('SQL token persistence', function () {
|
||||
initialTokens.forEach(async token => {
|
||||
await pool.query(
|
||||
`INSERT INTO pg_temp.${tableName} (token) VALUES ($1::text);`,
|
||||
[token],
|
||||
[token]
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -77,7 +77,7 @@ describe('SQL token persistence', function () {
|
||||
await persistence.noteTokenAdded(newToken)
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT token FROM pg_temp.${tableName};`,
|
||||
`SELECT token FROM pg_temp.${tableName};`
|
||||
)
|
||||
const savedTokens = result.rows.map(row => row.token)
|
||||
expect(savedTokens.sort()).to.deep.equal(expected)
|
||||
@@ -93,7 +93,7 @@ describe('SQL token persistence', function () {
|
||||
await persistence.noteTokenRemoved(toRemove)
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT token FROM pg_temp.${tableName};`,
|
||||
`SELECT token FROM pg_temp.${tableName};`
|
||||
)
|
||||
const savedTokens = result.rows.map(row => row.token)
|
||||
expect(savedTokens.sort()).to.deep.equal(expected)
|
||||
|
||||
@@ -28,14 +28,14 @@ export default class SqlTokenPersistence {
|
||||
async onTokenAdded(token) {
|
||||
return await this.pool.query(
|
||||
`INSERT INTO ${this.table} (token) VALUES ($1::text) ON CONFLICT (token) DO NOTHING;`,
|
||||
[token],
|
||||
[token]
|
||||
)
|
||||
}
|
||||
|
||||
async onTokenRemoved(token) {
|
||||
return await this.pool.query(
|
||||
`DELETE FROM ${this.table} WHERE token=$1::text;`,
|
||||
[token],
|
||||
[token]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@ describe('The token pool', function () {
|
||||
|
||||
it('should yield the expected tokens', function () {
|
||||
ids.forEach(id =>
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id)),
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id))
|
||||
)
|
||||
})
|
||||
|
||||
it('should repeat when reaching the end', function () {
|
||||
ids.forEach(id =>
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id)),
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id))
|
||||
)
|
||||
ids.forEach(id =>
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id)),
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id))
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const { fileMatch } = danger.git
|
||||
const documentation = fileMatch(
|
||||
'**/*.md',
|
||||
'frontend/docs/**',
|
||||
'frontend/src/**',
|
||||
'frontend/src/**'
|
||||
)
|
||||
const server = fileMatch('core/server/**.js', '!*.spec.js')
|
||||
const serverTests = fileMatch('core/server/**.spec.js')
|
||||
@@ -33,7 +33,7 @@ message(
|
||||
[
|
||||
':sparkles: Thanks for your contribution to Shields, ',
|
||||
`@${danger.github.pr.user.login}!`,
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
|
||||
const targetBranch = danger.github.pr.base.ref
|
||||
@@ -48,7 +48,7 @@ if (documentation.edited) {
|
||||
[
|
||||
'Thanks for contributing to our documentation. ',
|
||||
'We :heart: our [documentarians](http://www.writethedocs.org/)!',
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ if (server.modified && !serverTests.modified) {
|
||||
[
|
||||
'This PR modified the server but none of its tests. <br>',
|
||||
"That's okay so long as it's refactoring existing code.",
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ if (legacyHelpers.created) {
|
||||
[
|
||||
'This PR modified helper functions in `lib/` but not accompanying tests. <br>',
|
||||
"That's okay so long as it's refactoring existing code.",
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ if (logos.created) {
|
||||
'Please ensure your contribution follows our ',
|
||||
'[guidance](https://github.com/badges/shields/blob/master/doc/logos.md#contributing-logos) ',
|
||||
'for logo submissions.',
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ if (capitals.created || underscores.created) {
|
||||
[
|
||||
'JavaScript source files should be named with `kebab-case` ',
|
||||
'(dash-separated lowercase).',
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ if (allFiles.length > 100) {
|
||||
':books: Remember to ensure any changes to `config.private` ',
|
||||
`in \`${file}\` are reflected in the [server secrets documentation]`,
|
||||
'(https://github.com/badges/shields/blob/master/doc/server-secrets.md)',
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ if (allFiles.length > 100) {
|
||||
`Found 'assert' statement added in \`${file}\`. <br>`,
|
||||
'Please ensure tests are written using Chai ',
|
||||
'[expect syntax](http://chaijs.com/guide/styles/#expect)',
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ if (allFiles.length > 100) {
|
||||
[
|
||||
`Found import of '@hapi/joi' in \`${file}\`. <br>`,
|
||||
"Joi must be imported as 'joi'.",
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -168,7 +168,7 @@ affectedServices.forEach(service => {
|
||||
[
|
||||
`This PR modified service code for <kbd>${service}</kbd> but not its test code. <br>`,
|
||||
"That's okay so long as it's refactoring existing code.",
|
||||
].join(''),
|
||||
].join('')
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,7 +25,7 @@ and learn about the [GitHub workflow](http://try.github.io/).
|
||||
|
||||
#### Node, NPM
|
||||
|
||||
Node >=16 and NPM 9.x is required. If you don't already have them,
|
||||
Node >=16 and NPM >=8 is required. If you don't already have them,
|
||||
install node and npm: https://nodejs.org/en/download/
|
||||
|
||||
### Setup a dev install
|
||||
|
||||
35
doc/logos.md
35
doc/logos.md
@@ -1,6 +1,38 @@
|
||||
# Logos
|
||||
|
||||
For documentation on using logos, see https://shields.io/docs/logos
|
||||
## Using Logos
|
||||
|
||||
### SimpleIcons
|
||||
|
||||
We support a wide range of logos via [SimpleIcons][]. They should be referenced by the logo slug e.g:
|
||||
|
||||
 - https://img.shields.io/npm/v/npm.svg?logo=nodedotjs
|
||||
|
||||
The set of Simple Icon slugs can be found in the [slugs.md](https://github.com/simple-icons/simple-icons/blob/develop/slugs.md) file in the Simple Icons repository. NB - the Simple Icons site and that slugs.md page may at times contain new icons that haven't yet been pulled into the Shields.io runtime. More information on how and when we incorporate icon updates can be found [here](https://github.com/badges/shields/discussions/5369).
|
||||
|
||||
### Shields logos
|
||||
|
||||
We also maintain a small number of custom logos for some services: https://github.com/badges/shields/tree/master/logo They can also be referenced by name and take preference to SimpleIcons e.g:
|
||||
|
||||
 - https://img.shields.io/npm/v/npm.svg?logo=npm
|
||||
|
||||
### Custom Logos
|
||||
|
||||
Any custom logo can be passed in a URL parameter by base64 encoding it. e.g:
|
||||
|
||||
 - https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+
|
||||
|
||||
### logoColor parameter
|
||||
|
||||
The `logoColor` param can be used to set the color of the logo. Hex, rgb, rgba, hsl, hsla and css named colors can all be used. For SimpleIcons named logos (which are monochrome), the color will be applied to the SimpleIcons logo.
|
||||
|
||||
-  - https://img.shields.io/badge/logo-javascript-blue?logo=javascript
|
||||
-  - https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5
|
||||
|
||||
In the case where Shields hosts a custom multi-colored logo, if the `logoColor` param is passed, the corresponding SimpleIcons logo will be substituted and colored.
|
||||
|
||||
-  - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab
|
||||
-  - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white
|
||||
|
||||
## Contributing Logos
|
||||
|
||||
@@ -37,6 +69,7 @@ If you are submitting a pull request for a custom logo, please:
|
||||
|
||||
We try to ensure our logos are compliant with brand guidelines. If one of our custom logos does not conform to the necessary brand guidelines, please open an issue on the [shields.io tracker](https://github.com/badges/shields/issues) and we'll work with you to resolve it. If a logo from the simple-icons set does not conform to the relevant brand guidelines, please open an issue on the [simple-icons tracker](https://github.com/simple-icons/simple-icons/issues) first.
|
||||
|
||||
[simpleicons]: https://simpleicons.org/
|
||||
[simple-icons github]: https://github.com/simple-icons/simple-icons
|
||||
[svgo]: https://github.com/svg/svgo
|
||||
[svgomg]: https://jakearchibald.github.io/svgomg/
|
||||
|
||||
@@ -186,7 +186,7 @@ t.create('Build status (private application)')
|
||||
.intercept(nock =>
|
||||
nock('https://app.wercker.com/api/v3/applications/')
|
||||
.get('/wercker/go-wercker-api/builds?limit=1')
|
||||
.reply(401),
|
||||
.reply(401)
|
||||
)
|
||||
.expectBadge({ label: 'build', message: 'private application not supported' })
|
||||
```
|
||||
@@ -227,7 +227,7 @@ t.create('Build passed')
|
||||
.intercept(nock =>
|
||||
nock('https://app.wercker.com/api/v3/applications/')
|
||||
.get('/wercker/go-wercker-api/builds?limit=1')
|
||||
.reply(200, [{ status: 'finished', result: 'passed' }]),
|
||||
.reply(200, [{ status: 'finished', result: 'passed' }])
|
||||
)
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
@@ -240,7 +240,7 @@ t.create('Build failed')
|
||||
.intercept(nock =>
|
||||
nock('https://app.wercker.com/api/v3/applications/')
|
||||
.get('/wercker/go-wercker-api/builds?limit=1')
|
||||
.reply(200, [{ status: 'finished', result: 'failed' }]),
|
||||
.reply(200, [{ status: 'finished', result: 'failed' }])
|
||||
)
|
||||
.expectBadge({ label: 'build', message: 'failed', color: 'red' })
|
||||
```
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
This documentation has moved to https://shields.io/docs/static-badges
|
||||
# Static Badges
|
||||
|
||||
It is possible to use shields.io to make a wide variety of badges displaying static text and/or logos. For example:
|
||||
|
||||
-  - https://img.shields.io/badge/any%20text-you%20like-blue
|
||||
-  - https://img.shields.io/badge/just%20the%20message-8A2BE2
|
||||
-  - https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge
|
||||
-  - https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript
|
||||
|
||||
Full documentation of styles and parameters: https://shields.io/#styles
|
||||
|
||||
More documentation on logos: https://github.com/badges/shields/blob/master/doc/logos.md
|
||||
|
||||
@@ -18,7 +18,7 @@ after('shut down the server', async function () {
|
||||
it('should render a badge', async function () {
|
||||
this.timeout('30s')
|
||||
const { statusCode, body } = await got(
|
||||
'http://localhost:1111/badge/fruit-apple-green.svg',
|
||||
'http://localhost:1111/badge/fruit-apple-green.svg'
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(body).to.satisfy(isSvg).and.to.include('fruit').and.to.include('apple')
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
slug: new-frontend
|
||||
title: We launched a new frontend
|
||||
authors:
|
||||
name: chris48s
|
||||
title: Shields.io Core Team
|
||||
url: https://github.com/chris48s
|
||||
image_url: https://avatars.githubusercontent.com/u/6025893
|
||||
tags: []
|
||||
---
|
||||
|
||||
Alongside the general visual refresh and improvements to look and feel, our new frontend has allowed us to address a number of long-standing feature requests and enhancements:
|
||||
|
||||
- Clearer and more discoverable documentation for our [static](https://shields.io/badges/static-badge), dynamic [json](https://shields.io/badges/dynamic-json-badge)/[xml](https://shields.io/badges/dynamic-xml-badge)/[yaml](https://shields.io/badges/dynamic-yaml-badge) and [endpoint](https://shields.io/badges/endpoint-badge) badges
|
||||
- Improved badge builder interface, with all optional query parameters included in the builder for each badge
|
||||
- Each badge now has its own documentation page, which we can link to. e.g: [https://shields.io/badges/discord](https://shields.io/badges/discord)
|
||||
- Light/dark mode themes
|
||||
- Improved search
|
||||
- Documentation for individual path and query parameters
|
||||
|
||||
The new site also comes with big maintenance benefits for the core team. We rely heavily on [docusaurus](https://docusaurus.io/), [docusaurus-openapi](https://github.com/cloud-annotations/docusaurus-openapi), and [docusaurus-search-local](https://github.com/easyops-cn/docusaurus-search-local). This moves us to a mostly declarative setup, massively reducing the amount of custom frontend code we maintain ourselves.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
slug: tag-filter
|
||||
title: Applying filters to GitHub Tag and Release badges
|
||||
authors:
|
||||
name: chris48s
|
||||
title: Shields.io Core Team
|
||||
url: https://github.com/chris48s
|
||||
image_url: https://avatars.githubusercontent.com/u/6025893
|
||||
tags: []
|
||||
---
|
||||
|
||||
We recently shipped a feature which allows you to pass an arbitrary filter to the GitHub tag and release badges. The `filter` param can be used to apply a filter to the project's tag or release names before selecting the latest from the list. Two constructs are available: `*` is a wildcard matching zero or more characters, and if the pattern starts with a `!`, the whole pattern is negated.
|
||||
|
||||
To give an example of how this might be useful, we create two types of tags on our GitHub repo: https://github.com/badges/shields/tags There are tags in the format `major.minor.patch` which correspond to our [NPM package releases](https://www.npmjs.com/package/badge-maker?activeTab=versions) and tags in the format `server-YYYY-MM-DD` that correspond to our [docker snapshot releases](https://registry.hub.docker.com/r/shieldsio/shields/tags?page=1&ordering=last_updated).
|
||||
|
||||
In our case, this would allow us to make a badge that applies the filter `!server-*` to filter out the snapshot tags and just select the latest package tag.
|
||||
|
||||
-  - https://img.shields.io/github/v/tag/badges/shields
|
||||
-  - https://img.shields.io/github/v/tag/badges/shields?filter=%21server-%2A
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Intro
|
||||
|
||||
Shields.io is a service for concise, consistent, and legible badges, which can easily be included in GitHub readmes or any other web page. The service supports dozens of continuous integration services, package registries, distributions, app stores, social networks, code coverage services, and code analysis services. It is used by some of the world's most popular open-source projects.
|
||||
|
||||
Browse a [complete list of badges](/badges) and locate a particular badge by using the search bar or by browsing the categories.
|
||||
|
||||
Use the builder to fill in required path parameters for that badge type (like your username or repo) and optionally customize (label, colors etc.). And it's ready for use! Copy your badge url or code snippet, which can then be added to places like your GitHub readme files or other web pages.
|
||||
|
||||

|
||||
5
frontend/docs/intro.md
Normal file
5
frontend/docs/intro.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# TODO
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Logos
|
||||
|
||||
## SimpleIcons
|
||||
|
||||
We support a wide range of logos via [SimpleIcons](https://simpleicons.org/). All simple-icons are referenced using icon slugs. e.g:
|
||||
|
||||
 - https://img.shields.io/npm/v/npm.svg?logo=nodedotjs
|
||||
|
||||
You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository. NB - the Simple Icons site and slugs.md page may at times contain new icons that haven't yet been pulled into Shields.io yet. More information on how and when we incorporate icon updates can be found [here](https://github.com/badges/shields/discussions/5369).
|
||||
|
||||
## Shields logos
|
||||
|
||||
We also maintain a small number of custom logos for a handful of services: https://github.com/badges/shields/tree/master/logo They can also be referenced by name and take preference to SimpleIcons e.g:
|
||||
|
||||
 - https://img.shields.io/npm/v/npm.svg?logo=npm
|
||||
|
||||
## Custom Logos
|
||||
|
||||
Any custom logo can be passed in a URL parameter by base64 encoding it. e.g:
|
||||
|
||||
 - https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+
|
||||
|
||||
## logoColor parameter
|
||||
|
||||
The `logoColor` param can be used to set the color of the logo. Hex, rgb, rgba, hsl, hsla and css named colors can all be used. For SimpleIcons named logos (which are monochrome), the color will be applied to the SimpleIcons logo.
|
||||
|
||||
-  - https://img.shields.io/badge/logo-javascript-blue?logo=javascript
|
||||
-  - https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5
|
||||
|
||||
In the case where Shields hosts a custom multi-colored logo, if the `logoColor` param is passed, the corresponding SimpleIcons logo will be substituted and colored.
|
||||
|
||||
-  - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab
|
||||
-  - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white
|
||||
@@ -1,13 +0,0 @@
|
||||
# Static Badges
|
||||
|
||||
It is possible to use shields.io to make a wide variety of badges displaying static text and/or logos. For example:
|
||||
|
||||
-  - https://img.shields.io/badge/any%20text-you%20like-blue
|
||||
-  - https://img.shields.io/badge/just%20the%20message-8A2BE2
|
||||
-  - https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge
|
||||
-  - https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript
|
||||
|
||||
For more info, see:
|
||||
|
||||
- [Static badge builder](/badges/static-badge), including full documentation of styles and parameters
|
||||
- [Logos](/docs/logos)
|
||||
@@ -1,5 +1,5 @@
|
||||
const lightCodeTheme = require('prism-react-renderer').themes.github
|
||||
const darkCodeTheme = require('prism-react-renderer').themes.dracula
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github')
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula')
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
@@ -31,11 +31,11 @@ const config = {
|
||||
({
|
||||
docs: {
|
||||
sidebarPath: require.resolve('./sidebars.cjs'),
|
||||
editUrl: 'https://github.com/badges/shields/tree/master/frontend',
|
||||
editUrl: 'https://github.com/badges/shields/',
|
||||
},
|
||||
blog: {
|
||||
showReadingTime: true,
|
||||
editUrl: 'https://github.com/badges/shields/tree/master/frontend',
|
||||
editUrl: 'https://github.com/badges/shields/',
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
@@ -60,13 +60,7 @@ const config = {
|
||||
},
|
||||
items: [
|
||||
{ to: '/badges', label: 'Badges', position: 'left' },
|
||||
{
|
||||
to: '/docs',
|
||||
label: 'Documentation',
|
||||
position: 'left',
|
||||
},
|
||||
{ to: '/community', label: 'Community', position: 'left' },
|
||||
{ to: '/blog', label: 'Blog', position: 'left' },
|
||||
{
|
||||
href: 'https://github.com/badges/shields',
|
||||
label: 'GitHub',
|
||||
|
||||
@@ -35,13 +35,7 @@ const FeatureList = [
|
||||
description: (
|
||||
<>
|
||||
Render badges in your own application using our{' '}
|
||||
<a
|
||||
href="https://www.npmjs.com/package/badge-maker"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
NPM library
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/badge-maker">NPM library</a>
|
||||
<br />
|
||||
<code>npm install badge-maker</code>
|
||||
</>
|
||||
@@ -52,11 +46,7 @@ const FeatureList = [
|
||||
description: (
|
||||
<>
|
||||
Host a shields instance behind your firewall with our{' '}
|
||||
<a
|
||||
href="https://registry.hub.docker.com/r/shieldsio/shields/"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://registry.hub.docker.com/r/shieldsio/shields/">
|
||||
docker image
|
||||
</a>
|
||||
<br />
|
||||
@@ -69,14 +59,8 @@ const FeatureList = [
|
||||
description: (
|
||||
<>
|
||||
Please consider{' '}
|
||||
<a
|
||||
href="https://opencollective.com/shields"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
donating
|
||||
</a>{' '}
|
||||
to sustain our activities
|
||||
<a href="https://opencollective.com/shields">donating</a> to sustain our
|
||||
activities
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -26,27 +26,3 @@
|
||||
html[data-theme="dark"] .docusaurus-highlight-code-line {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.opencollective-image {
|
||||
color-scheme: initial;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: remove these three styles when
|
||||
we can upgrade to docusaurus-theme-openapi@0.6.5
|
||||
*/
|
||||
|
||||
input[type="text"], :not(#fakeID#fakeId#fakeID) select {
|
||||
border-color: var(--ifm-color-primary-lightest);
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
div.api-code-tab-group {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.api-code-tab-group button.api-code-tab {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
@@ -22,10 +22,7 @@ Shields.io is possible thanks to the people and companies who donate money, serv
|
||||
💵 These organisations help keep shields running by donating on OpenCollective. Your organisation can support this project by <a href="https://opencollective.com/shields#sponsor">becoming a sponsor </a>. Your logo will show up here with a link to your website.
|
||||
|
||||
<p>
|
||||
<object
|
||||
data="https://opencollective.com/shields/sponsors.svg?avatarHeight=80&width=600"
|
||||
class="opencollective-image"
|
||||
></object>
|
||||
<object data="https://opencollective.com/shields/sponsors.svg?avatarHeight=80&width=600" />
|
||||
</p>
|
||||
|
||||
## Backers
|
||||
@@ -33,10 +30,7 @@ Shields.io is possible thanks to the people and companies who donate money, serv
|
||||
💵 Thank you to all our backers who help keep shields running by donating on OpenCollective. You can support this project by <a href="https://opencollective.com/shields#backer">becoming a backer</a>.
|
||||
|
||||
<p>
|
||||
<object
|
||||
data="https://opencollective.com/shields/backers.svg?width=600"
|
||||
class="opencollective-image">
|
||||
</object>
|
||||
<object data="https://opencollective.com/shields/backers.svg?width=600" />
|
||||
</p>
|
||||
|
||||
## Contributors
|
||||
@@ -44,10 +38,7 @@ Shields.io is possible thanks to the people and companies who donate money, serv
|
||||
🙏 This project exists thanks to all the nice people who contribute their time to work on the project.
|
||||
|
||||
<p>
|
||||
<object
|
||||
data="https://opencollective.com/shields/contributors.svg?width=600"
|
||||
class="opencollective-image"
|
||||
></object>
|
||||
<object data="https://opencollective.com/shields/contributors.svg?width=600" />
|
||||
</p>
|
||||
|
||||
✨ Shields is helped by these companies which provide a free plan for their product or service:
|
||||
|
||||
@@ -27,8 +27,8 @@ export default function Home() {
|
||||
const { siteConfig } = useDocusaurusContext()
|
||||
return (
|
||||
<Layout
|
||||
description="Concise, consistent, and legible badges"
|
||||
title={siteConfig.title}
|
||||
description="Description will go into a meta tag in <head />"
|
||||
title={`${siteConfig.title}`}
|
||||
>
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useRef, useState, useEffect } from 'react'
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
||||
import clsx from 'clsx'
|
||||
import codegen from 'postman-code-generators'
|
||||
import { Highlight } from 'prism-react-renderer'
|
||||
import Highlight, { defaultProps } from 'prism-react-renderer'
|
||||
import { useTypedSelector } from '@theme/ApiDemoPanel/hooks'
|
||||
import buildPostmanRequest from '@theme/ApiDemoPanel/buildPostmanRequest'
|
||||
import FloatingButton from '@theme/ApiDemoPanel/FloatingButton'
|
||||
@@ -183,11 +183,11 @@ function Curl({ postman, codeSamples }) {
|
||||
}
|
||||
|
||||
setCodeText(snippet)
|
||||
},
|
||||
}
|
||||
)
|
||||
} else if (language && !!language.source) {
|
||||
setCodeText(
|
||||
language.source.replace('$url', postmanRequest.url.toString()),
|
||||
language.source.replace('$url', postmanRequest.url.toString())
|
||||
)
|
||||
} else {
|
||||
setCodeText('')
|
||||
@@ -230,7 +230,7 @@ function Curl({ postman, codeSamples }) {
|
||||
className={clsx(
|
||||
language === lang ? styles.selected : undefined,
|
||||
language === lang ? 'api-code-tab--active' : undefined,
|
||||
'api-code-tab',
|
||||
'api-code-tab'
|
||||
)}
|
||||
key={lang.tabName || lang.label}
|
||||
onClick={() => setLanguage(lang)}
|
||||
@@ -241,6 +241,7 @@ function Curl({ postman, codeSamples }) {
|
||||
</div>
|
||||
|
||||
<Highlight
|
||||
{...defaultProps}
|
||||
code={codeText}
|
||||
language={language.highlight || language.lang}
|
||||
theme={languageTheme}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
@@ -9,7 +9,7 @@ function loadLogos() {
|
||||
const logoDir = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'..',
|
||||
'logo',
|
||||
'logo'
|
||||
)
|
||||
const logoFiles = fs.readdirSync(logoDir)
|
||||
logoFiles.forEach(filename => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user