-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to Express [*****] #7877
Conversation
|
This pull request introduces 1 alert when merging 78b886c into 560d267 - view on LGTM.com new alerts:
|
@@ -24,22 +23,6 @@ module.exports = function makeBadge({ | |||
label = `${label}`.trim() | |||
message = `${message}`.trim() | |||
|
|||
// This ought to be the responsibility of the server, not `makeBadge`. | |||
if (format === 'json') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've argued before that this functionality doesn't belong in the badge library as the particular JSON schema we generate here (with name
and value
) is a legacy quirk of shields.io, and not something we particularly want users to replace elsewhere. For the moment I went ahead and moved this functionality to the server. It's now handled by makeJsonBadge()
which is invoked from a conditional in the request handler in base.js
.
If we keep this we'll probably want to add normalizeColor to the package's public interface, or alternatively a normalizeBadgeData()
function, and then we could start dogfooding the public makeBadge()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't read over the code for this bit at all yet. I agree with the objective. Has anything significant has changed since #4980 in terms of the implementation, specifically #4980 (comment) ? I will have a look through it. If a slightly different configuration of not eating our own dogfood is necessary to get off scoutcamp, this shouldn't be a blocker but until we do #4947 and #4949 I suspect all we can really do is move from one undesirable implementation to another. I do accept #4947 and #4949 have been open for 2 years and none of us are actively working on them.
As a note, we may want to consider documenting it as a non-BC change for badge-maker, even though format='json'
is undocumented. The next badge-maker will be a major release anyway due to dropping node 10.
describe('The badge generator', function () { | ||
describe('color test', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly, but not completely, these duplicate tests in color.spec.js
. We may want to go through them line by line and see if there are any here we should port over. With the format
parameter removed, this behavior can no longer be tested here.
}) | ||
|
||
it('should replace undefined svg badge style with "flat"', function () { | ||
const jsonBadgeWithUnknownStyle = makeBadge({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inline
}) | ||
|
||
describe('JSON', function () { | ||
it('should produce the expected JSON', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove JSON tests
style: 'social', | ||
}) | ||
) | ||
.to.include('""') | ||
.to.include('></text>') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SVG equivalent
@@ -112,7 +110,9 @@ describe('The server', function () { | |||
`${baseUrl}:fruit-apple-green.json` | |||
) | |||
expect(statusCode).to.equal(200) | |||
expect(headers['content-type']).to.equal('application/json') | |||
expect(headers['content-type']).to.equal( | |||
'application/json; charset=utf-8' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Charset added by Express
expect(body) | ||
.to.satisfy(isSvg) | ||
.and.to.include('410') | ||
.and.to.include('jpg no longer available') | ||
}) | ||
|
||
it('should return cors header for the request', async function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This duplicates a test a bit higher up in this context
[`core/server/server.js`][core/server/server], is based on [Express][]. | ||
It creates an http server, sets up helpers for token persistence and | ||
monitoring. Then it loads all the services, injecting dependencies as it | ||
asks each one to register its route with the Express app. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a rough effort to update this, though it may make sense for someone to take a pass through later. This whole process is much more direct than before.
@@ -25,7 +25,6 @@ | |||
"@fontsource/lekton": "^4.5.6", | |||
"@renovate/pep440": "^1.0.0", | |||
"@sentry/node": "^6.19.6", | |||
"@shields_io/camp": "^18.1.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💁🏼 🎩
return end('GitHub OAuth authentication failed to provide a code.') | ||
} | ||
app.post('/github-auth/done', multer().none(), async (req, res) => { | ||
const code = (req.body ?? {}).code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been tested yet
In the first service test run there are some interesting failures. One issue is that the JSON badge no longer strips leading and trailing whitespace from |
}) | ||
}) | ||
|
||
context('No named params are declared', function () { | ||
const { regex, captureNames } = prepareRoute({ | ||
base: 'foo', | ||
format: '(?:[^/]+)', | ||
format: '(?:[^/]+?)', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To correctly detect .svg
and .json
as intended, this regex needs to be lazy.
function setRoutes(allowedOrigin, githubApiProvider, server) { | ||
server.ajax.on('suggest/v1', (data, end, ask) => { | ||
function setRoutes(allowedOrigin, githubApiProvider, app) { | ||
app.get('/[$]suggest/v1', (req, res) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Scoutcamp AJAX routes implicitly started with $. For some reason the dollar needs to be escaped for Express.
if (origin) { | ||
let host | ||
try { | ||
host = new URL(origin).hostname | ||
} catch (e) { | ||
ask.res.setHeader('Access-Control-Allow-Origin', 'null') | ||
end({ err: 'Disallowed' }) | ||
res.setHeader('Access-Control-Allow-Origin', 'null') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I read this I wonder if it works, especially since we're setting Access-Control-Allow-Origin: *
in server.js.
Thanks for taking this on. This aspect of the codebase and the reliance on ScoutCamp is one of the biggest pain points of this codebase so it is really good to be making some progress on this :) I haven't done a full review of this, but I have checked it out locally and skimmed the diff. The main issue I hit is if I run
and won't start the frontend, so I've only been able to test the badge server. Any idea what is happening there? There were a few bits I wanted to specifically check as possible edge cases:
I'll try and have a more detailed look at this next week. |
@@ -306,87 +297,78 @@ class Server { | |||
|
|||
// See https://www.viget.com/articles/heroku-cloudflare-the-right-way/ | |||
requireCloudflare() { | |||
// Set `req.ip`, which is expected by `cloudflareMiddleware()`. This is set | |||
// by Express but not Scoutcamp. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll definitely need to add back the special case for fly.io before we can merge/deploy this. It definitely doesn't work with the default setup.
On the heroku case, I'm slightly in 2 minds whether to just bin that on the basis we're not using it or keep it on the basis people who are self-hosting might be relying on it. In any case, we added this in #5712 because we deployed to Heroku using the default behaviour of cloudflareMiddleware()
and it didn't work.
app.use( | ||
express.static(this.config.public.documentRoot, { | ||
// Since express's `maxAge` parameter sets `Cache-Control: public`, set | ||
// the headers manually insetad. | ||
cacheControl: false, | ||
setHeaders: res => | ||
res.setHeader('Cache-Control', 'max-age=300, s-maxage=300'), | ||
}) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this replacing staticMaxAge
?
badges/sc@dc89a3a
const serverResponsive = setTimeout(() => { | ||
serverUnresponsive = true | ||
ask.res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate') | ||
const badgeData = coalesceBadge( | ||
filteredQueryParams, | ||
{ label: 'vendor', message: 'unresponsive' }, | ||
{} | ||
) | ||
const svg = makeBadge(badgeData) | ||
const extension = (match.slice(-1)[0] || '.svg').replace(/^\./, '') | ||
setCacheHeadersOnResponse(ask.res) | ||
makeSend(extension, ask.res, end)(svg) | ||
}, 25000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we've just totally dropped this in favour of express this.server.setTimeout()
?
@@ -24,22 +23,6 @@ module.exports = function makeBadge({ | |||
label = `${label}`.trim() | |||
message = `${message}`.trim() | |||
|
|||
// This ought to be the responsibility of the server, not `makeBadge`. | |||
if (format === 'json') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't read over the code for this bit at all yet. I agree with the objective. Has anything significant has changed since #4980 in terms of the implementation, specifically #4980 (comment) ? I will have a look through it. If a slightly different configuration of not eating our own dogfood is necessary to get off scoutcamp, this shouldn't be a blocker but until we do #4947 and #4949 I suspect all we can really do is move from one undesirable implementation to another. I do accept #4947 and #4949 have been open for 2 years and none of us are actively working on them.
As a note, we may want to consider documenting it as a non-BC change for badge-maker, even though format='json'
is undocumented. The next badge-maker will be a major release anyway due to dropping node 10.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did another slightly more detailed pass over this and left some more comments
next() | ||
} | ||
}, | ||
this.makeExpressHandler(serviceContext, serviceConfig) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is dropping the early return
in the 304 case intended here?
It looks like we are sticking with early return in core/base-service/redirector.js
) | ||
} | ||
|
||
it('should set the expires header to current time + defaultCacheLengthSeconds', async function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably do want to replicate at lest some of these tests, particularly for the cache headers. What level do you think would make most sense to test this at with the revised architecture?
let urlSuffix = ask.uri.search || '' | ||
let urlSuffix = url.parse(req.url).search ?? '' // eslint-disable-line node/no-deprecated-api |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this with let urlSuffix = new url.URL(req.url).search
instead of introducing a deprecated call here? I think that should be the same.
// TODO It would be nice if this were 404 or 410. | ||
expect(statusCode).to.equal(200) | ||
expect(statusCode).to.equal(410) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
await new Promise(resolve => camp.on('listening', () => resolve())) | ||
this.server.setTimeout(this.config.public.requestTimeoutSeconds * 1000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If we're going to move to just dropping the connection on timeout, I think we can just get rid of the
requestTimeoutMaxAgeSeconds
setting as it won't do anything requestTimeoutSeconds
is an optional setting (for self hosting users). We should handle the case where it is not set.
Thanks for the comments. Will nudge this forward when I can! |
Given that there hasn't been any activity for a while, I'm going to close this pull request. Feel free to ping us or reopen it if you get a chance to look into it again. 😉 If others think that repository level projects would be an option and want to pick this up, you can simply fetch the commits by running git fetch origin pull/7877/head:pr-7877. Change the implementation to target repository level projects and open a new pull request with a co-authored trailer included in the commit message to give attribution to the original author. |
It is still my ambition to pick this up and finish it at some point, but agree that there is limited value in keeping the PR hanging around |
There's surely a bit of cleanup to do here, but thought it worth getting some 👀 on this.
I'd welcome help updating and improving the documentation, though that could also be done in a follow-on PR.
An area I've not yet addressed is query parameters. Scoutcamp used to turn the params into numbers, which I imagine Express does not do. It means we probably can simplify places where we've got
Joi.alternatives().try(Joi.string(), Joi.number())
in the query param schemas.There are surely bugs in this, and edge cases. I removed a few tests in
legacy-request-handler.spec.js
which we may want to rewrite elsewhere.We should test this out (maybe even on Fly), in particular things like suggest and token acceptance.
I would be surprised if performance were a problem, though we may want to experiment with this under load to make sure.
Closes #3328
Closes #3368
Closes #4948