Skip to content
Snippets Groups Projects
Commit 073b611d authored by richard.petersen's avatar richard.petersen :sailboat:
Browse files

Changed: Replace express with fastify to improve overall performance

parent fd3764d7
No related branches found
No related tags found
No related merge requests found
Showing
with 173 additions and 145 deletions
......@@ -3,5 +3,8 @@ CACHE_TTL=30000
PORT=8080
LOG_LEVEL=info
APP_ROOT=/
EXPOSE_API_DOCS=false
REDIS_PORT=6379
REDIS_DB=0
REDIS_PREFIX=ui-middleware
......@@ -46,14 +46,14 @@ spec:
{{- if .Values.probe.liveness.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.appRoot }}healthy
path: live
port: http
{{- omit .Values.probe.liveness "enabled" | toYaml | nindent 12 }}
{{- end }}
{{- if .Values.probe.readiness.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.appRoot }}ready
path: ready
port: http
{{- omit .Values.probe.readiness "enabled" | toYaml | nindent 12 }}
{{- end }}
......
......@@ -28,7 +28,7 @@ describe('File caching service', function () {
})
it('caches manifest data', async function () {
const response = await request(app).get('/manifests')
const response = await request(app.server).get('/manifests')
expect(response.statusCode).to.equal(200)
const version = response.headers.version
......@@ -37,11 +37,11 @@ describe('File caching service', function () {
})
it('caches html files', async function () {
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
const version = response.headers.version
const body = await client.getBuffer(getRedisKey({ version, name: '/index.html:body' }))
const body = (await client.getBuffer(getRedisKey({ version, name: '/index.html:body' }))) || ''
expect(gunzipSync(body).toString()).to.equal('<html><head></head><body>it\'s me</body></html>')
const meta = await client.get(getRedisKey({ version, name: '/index.html:meta' }))
expect(meta).to.equal('{"headers":{"content-type":"text/html","dependencies":false,"content-encoding":"gzip"}}')
......@@ -52,14 +52,14 @@ describe('File caching service', function () {
await client.set(getRedisKey({ version, name: '/demo.js:meta' }), '{"headers":{"content-type":"application/javascript","dependencies":false}}')
await client.set(getRedisKey({ version, name: '/demo.js:body' }), 'console.log("Demo")')
const response = await request(app).get('/demo.js').set('version', version)
const response = await request(app.server).get('/demo.js').set('version', version)
expect(response.statusCode).to.equal(200)
// just for testing purposes, delete the keys from redis to make sure, it is served from local cache
await client.del(getRedisKey({ version, name: '/demo.js:meta' }))
await client.del(getRedisKey({ version, name: '/demo.js:body' }))
const response2 = await request(app).get('/demo.js').set('version', version)
const response2 = await request(app.server).get('/demo.js').set('version', version)
expect(response2.statusCode).to.equal(200)
})
})
......@@ -11,7 +11,7 @@ describe('Configuration', function () {
beforeEach(async function () {
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random()
process.env.REDIS_PREFIX = Math.random().toString()
await client.flushdb()
mockConfig(config = { urls: ['http://ui-server/'] })
mockFetch({
......@@ -42,14 +42,14 @@ describe('Configuration', function () {
// need to do this with dynamic import such that the mocked config is used
await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues())
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.body).to.have.length(2)
config.urls = []
pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), '1234')
await new Promise(resolve => setTimeout(resolve, 200))
const response2 = await request(app).get('/meta')
const response2 = await request(app.server).get('/meta')
expect(response2.body).to.have.length(1)
})
})
......@@ -10,7 +10,7 @@ describe('Updates the version', function () {
beforeEach(async function () {
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random()
process.env.REDIS_PREFIX = Math.random().toString()
await client.flushdb()
mockConfig({ urls: ['http://ui-server/'] })
mockFetch({
......@@ -31,7 +31,7 @@ describe('Updates the version', function () {
afterEach(async function () {
td.reset()
process.env.CACHE_TTL = 30000
process.env.CACHE_TTL = '30000'
for (const queue of getQueues()) {
await closeQueue(queue.name)
}
......@@ -43,7 +43,7 @@ describe('Updates the version', function () {
// need to do this with dynamic import such that the mocked config is used
await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues())
const responseBeforeUpdate = await request(app).get('/index.html')
const responseBeforeUpdate = await request(app.server).get('/index.html')
expect(responseBeforeUpdate.statusCode).to.equal(200)
expect(responseBeforeUpdate.headers.version).to.equal('85101541')
......@@ -52,15 +52,15 @@ describe('Updates the version', function () {
// update is executed with the second iteration
expect(await getQueue('update-version').add({}).then(job => job.finished())).to.equal('85102502')
const responseAfterUpdate = await request(app).get('/index.html')
const responseAfterUpdate = await request(app.server).get('/index.html')
expect(responseAfterUpdate.statusCode).to.equal(200)
expect(responseAfterUpdate.headers.version).to.equal('85102502')
})
it('with automatically triggered job', async function () {
process.env.CACHE_TTL = 100
process.env.CACHE_TTL = '100'
const responseBeforeUpdate = await request(app).get('/index.html')
const responseBeforeUpdate = await request(app.server).get('/index.html')
expect(responseBeforeUpdate.statusCode).to.equal(200)
expect(responseBeforeUpdate.headers.version).to.equal('85101541')
......@@ -77,7 +77,7 @@ describe('Updates the version', function () {
resolve()
}))
const responseAfterUpdate = await request(app).get('/index.html')
const responseAfterUpdate = await request(app.server).get('/index.html')
expect(responseAfterUpdate.statusCode).to.equal(200)
expect(responseAfterUpdate.headers.version).to.equal('85102502')
})
......@@ -86,7 +86,7 @@ describe('Updates the version', function () {
// need to do this with dynamic import such that the mocked config is used
await import('../src/create-queues.js').then(({ default: createQueues }) => createQueues())
const responseBeforeUpdate = await request(app).get('/index.html')
const responseBeforeUpdate = await request(app.server).get('/index.html')
expect(responseBeforeUpdate.statusCode).to.equal(200)
expect(responseBeforeUpdate.headers.version).to.equal('85101541')
......@@ -94,7 +94,7 @@ describe('Updates the version', function () {
pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), '1234')
await new Promise(resolve => setTimeout(resolve, 10))
const responseAfterUpdate = await request(app).get('/index.html')
const responseAfterUpdate = await request(app.server).get('/index.html')
expect(responseAfterUpdate.statusCode).to.equal(200)
expect(responseAfterUpdate.headers.version).to.equal('1234')
})
......@@ -103,7 +103,7 @@ describe('Updates the version', function () {
beforeEach(async function () {
td.reset()
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random()
process.env.REDIS_PREFIX = Math.random().toString()
await client.flushdb()
// preconfigure redis
await client.set(getRedisKey({ name: 'latestVersion' }), '12345')
......@@ -126,7 +126,7 @@ describe('Updates the version', function () {
it('uses version from redis if present', async function () {
app = await mockApp()
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
expect(response.headers.version).to.equal('12345')
})
......
{
"typeAcquisition": {
"enable": true
},
"compilerOptions": {
"checkJs": true,
"target": "es2022",
"module": "es2022",
"moduleResolution": "node"
}
}
\ No newline at end of file
......@@ -18,17 +18,18 @@
"author": "Open-Xchange",
"license": "CC-BY-NC-SA-2.5",
"dependencies": {
"@cloudnative/health-connect": "^2.1.0",
"@fastify/formbody": "^7.1.0",
"@fastify/helmet": "^9.1.0",
"@fastify/swagger": "^7.4.1",
"@open-xchange/logging": "^0.0.11",
"@open-xchange/metrics": "^0.0.1",
"bull": "^4.8.5",
"dotenv-defaults": "^5.0.2",
"express": "^4.18.1",
"helmet": "^5.1.1",
"fastify": "^4.5.3",
"fastify-metrics": "^9.2.2",
"fastify-plugin": "^4.2.1",
"http-errors": "^2.0.0",
"ioredis": "^5.2.2",
"js-yaml": "^4.0.0",
"swagger-ui-express": "^4.5.0"
"js-yaml": "^4.0.0"
},
"devDependencies": {
"autocannon": "^7.9.0",
......
......@@ -38,14 +38,14 @@ describe('JS files with dependencies contain events', function () {
})
it('javascript file contains dispatcher for dependencies', async function () {
const response = await request(app).get('/index.html.js')
const response = await request(app.server).get('/index.html.js')
expect(response.statusCode).to.equal(200)
expect(response.headers.dependencies).to.equal('main.css')
expect(response.text).to.equal('console.log("this is index.html.js")\n/*injected by ui-middleware*/document.dispatchEvent(new CustomEvent("load-css",{detail:{css:["main.css"]}}))')
})
it('javascript files from different versions have corrent dependencies', async function () {
const r1 = await request(app).get('/index.html.js')
const r1 = await request(app.server).get('/index.html.js')
expect(r1.headers.dependencies).to.equal('main.css')
mockFetchConfig['http://ui-server']['/manifest.json'] = generateSimpleViteManifest({
......@@ -65,16 +65,16 @@ describe('JS files with dependencies contain events', function () {
await updateVersionProcessor()
})
const r2 = await request(app).get('/index.html.js')
const r2 = await request(app.server).get('/index.html.js')
expect(r2.headers.dependencies).to.equal('other.css')
const r3 = await request(app).get('/index.html.js').set('version', r1.headers.version)
const r3 = await request(app.server).get('/index.html.js').set('version', r1.headers.version)
expect(r3.headers.dependencies).to.equal('main.css')
const r4 = await request(app).get('/index.html.js').set('version', r2.headers.version)
const r4 = await request(app.server).get('/index.html.js').set('version', r2.headers.version)
expect(r4.headers.dependencies).to.equal('other.css')
const r5 = await request(app).get('/index.html.js')
const r5 = await request(app.server).get('/index.html.js')
expect(r5.headers.dependencies).to.equal('other.css')
})
})
......@@ -50,7 +50,7 @@ describe('File caching service', function () {
return new Response(image, {
headers: {
'Content-Type': 'image/png',
'Content-Length': imageStat.size
'Content-Length': imageStat.size.toString()
}
})
}
......@@ -65,44 +65,44 @@ describe('File caching service', function () {
})
it('serves files defined in manifest.json file', async function () {
const response = await request(app).get('/example.js')
const response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(response.headers['content-type']).to.equal('application/javascript; charset=utf-8')
expect(response.headers['content-type']).to.equal('application/javascript')
expect(response.text).to.equal('this is example')
// expect(response.headers['content-security-policy']).to.contain('sha256-NzZhMTE2Njc2YTgyNTZmZTdlZGVjZDU3YTNmYzRjNmM1OWZkMTI2NjRkYzZmMWM3YTkwMGU3ZTdhNDlhZmVlMwo=')
const response2 = await request(app).get('/test.txt')
const response2 = await request(app.server).get('/test.txt')
expect(response2.statusCode).to.equal(200)
expect(response2.headers['content-type']).to.equal('text/plain; charset=utf-8')
expect(response2.headers['content-type']).to.equal('text/plain')
expect(response2.text).to.equal('this is test')
})
it('serves css files', async function () {
const response = await request(app).get('/main.css')
const response = await request(app.server).get('/main.css')
expect(response.statusCode).to.equal(200)
expect(response.headers['content-type']).to.equal('text/css; charset=utf-8')
expect(response.headers['content-type']).to.equal('text/css')
// expect(response.headers['content-security-policy']).to.contain('sha256-YjRiYWRlYTVhYmM5ZTZkNjE2ZGM4YjcwZWRlNzUxMmU0YjgxY2UxMWExOTI2ZjM1NzM1M2Y2MWJjNmUwMmZjMwo=')
})
it('serves / as index.html', async function () {
const response = await request(app).get('/')
const response = await request(app.server).get('/')
expect(response.statusCode).to.equal(200)
expect(response.headers['content-type']).to.equal('text/html; charset=utf-8')
expect(response.headers['content-type']).to.equal('text/html')
expect(response.text).to.equal('<html><head></head><body>it\'s me</body></html>')
})
it('directly fetches files not referenced in manifest.json files from the upstream servers', async function () {
const response = await request(app).get('/favicon.ico')
const response = await request(app.server).get('/favicon.ico')
expect(response.statusCode).to.equal(200)
expect(response.text).to.equal('not really a favicon, though')
})
it('returns 404 if file can not be resolved', async function () {
const response = await request(app).get('/unknown-file.txt')
const response = await request(app.server).get('/unknown-file.txt')
expect(response.statusCode).to.equal(404)
})
it('serves binary files', async function () {
const response = await request(app).get('/image.png')
const response = await request(app.server).get('/image.png')
expect(response.statusCode).to.equal(200)
expect(response.body.length === imageStat.size)
expect(response.body).to.deep.equal(image)
......@@ -121,10 +121,10 @@ describe('File caching service', function () {
app = await mockApp()
expect(spy.callCount).to.equal(0)
let response = await request(app).get('/example.js')
let response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
response = await request(app).get('/example.js')
response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
})
......@@ -142,14 +142,14 @@ describe('File caching service', function () {
app = await mockApp()
expect(spy.callCount).to.equal(0)
let response = await request(app).get('/example.js')
let response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
// delete file from redis
await redis.client.del(`${response.headers.version}:/example.js:body`)
await redis.client.del(`${response.headers.version}:/example.js:meta`)
// and fetch once more
response = await request(app).get('/example.js')
response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
})
......@@ -163,7 +163,7 @@ describe('File caching service', function () {
return new Response(image, {
headers: {
'Content-Type': 'image/png',
'Content-Length': imageStat.size
'Content-Length': imageStat.size.toString()
}
})
})
......@@ -171,11 +171,11 @@ describe('File caching service', function () {
})
app = await mockApp()
expect(spy.callCount).to.equal(0)
let response = await request(app).get('/image.png')
let response = await request(app.server).get('/image.png')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.equal(image)
expect(spy.callCount).to.equal(1)
response = await request(app).get('/image.png')
response = await request(app.server).get('/image.png')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.equal(image)
expect(spy.callCount).to.equal(1)
......@@ -184,7 +184,7 @@ describe('File caching service', function () {
it('a file is not cached again, if loaded from cache', async function () {
const spy = sandbox.spy(redis.client, 'set')
let response = await request(app).get('/example.js')
let response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
// called 4 times.
......@@ -193,7 +193,7 @@ describe('File caching service', function () {
// two times for for example.js (meta and body)
expect(spy.callCount).to.equal(4)
response = await request(app).get('/example.js')
response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
// should still be called 4 times, because everything is in cache
......@@ -224,7 +224,7 @@ describe('File caching service', function () {
})
app = await mockApp()
const response = await request(app).get('/example.js')
const response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy1.callCount).to.equal(0)
expect(spy2.callCount).to.equal(1)
......@@ -245,25 +245,25 @@ describe('File caching service', function () {
})
app = await mockApp()
const response1 = await request(app).get('/example.js').set('version', 1234)
const response1 = await request(app.server).get('/example.js').set('version', '1234')
expect(response1.statusCode).to.equal(200)
expect(response1.text).to.equal('first')
const response2 = await request(app).get('/example.js')
const response2 = await request(app.server).get('/example.js')
expect(response2.statusCode).to.equal(200)
expect(response2.text).to.equal('second')
const latestVersion = response2.headers['latest-version']
const response3 = await request(app).get('/example.js').set('version', 1234)
const response3 = await request(app.server).get('/example.js').set('version', '1234')
expect(response3.statusCode).to.equal(200)
expect(response3.text).to.equal('first')
const response4 = await request(app).get('/example.js')
const response4 = await request(app.server).get('/example.js')
expect(response4.statusCode).to.equal(200)
expect(response4.text).to.equal('second')
const response5 = await request(app).get('/example.js').set('version', latestVersion)
const response5 = await request(app.server).get('/example.js').set('version', latestVersion)
expect(response5.statusCode).to.equal(200)
expect(response5.text).to.equal('second')
})
......@@ -282,8 +282,8 @@ describe('File caching service', function () {
expect(spy.callCount).to.equal(0)
const [res1, res2] = await Promise.all([
request(app).get('/example.js'),
request(app).get('/example.js')
request(app.server).get('/example.js'),
request(app.server).get('/example.js')
])
expect(res1.statusCode).to.equal(200)
expect(res2.statusCode).to.equal(200)
......@@ -305,8 +305,8 @@ describe('File caching service', function () {
expect(spy.callCount).to.equal(0)
const [res1, res2] = await Promise.all([
request(app).get('/manifests'),
request(app).get('/example.js')
request(app.server).get('/manifests'),
request(app.server).get('/example.js')
])
expect(res1.statusCode).to.equal(200)
expect(res2.statusCode).to.equal(200)
......@@ -340,8 +340,8 @@ describe('File caching service', function () {
expect(spy.callCount).to.equal(0)
const [res1, res2] = await Promise.all([
request(app).get('/example.js'),
request(app).get('/example.js')
request(app.server).get('/example.js'),
request(app.server).get('/example.js')
])
expect(res1.statusCode).to.equal(200)
expect(res2.statusCode).to.equal(200)
......@@ -350,7 +350,7 @@ describe('File caching service', function () {
})
it('serves files as gzip', async function () {
const response = await request(app).get('/example.js')
const response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(response.headers['content-encoding']).to.equal('gzip')
......
......@@ -41,20 +41,20 @@ describe('Responses contain custom headers', function () {
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
expect(response.headers.version).to.equal('3215668592')
})
it('serves requested version', async function () {
const response = await request(app).get('/index.html.js').set('version', '123456')
const response = await request(app.server).get('/index.html.js').set('version', '123456')
expect(response.statusCode).to.equal(200)
expect(response.headers.version).to.equal('123456')
expect(response.headers['latest-version']).to.equal('3215668592')
})
it('javascript file contains dependencies', async function () {
const response = await request(app).get('/index.html.js')
const response = await request(app.server).get('/index.html.js')
expect(response.statusCode).to.equal(200)
expect(response.headers.dependencies).to.equal('main.css')
})
......@@ -78,7 +78,7 @@ describe('Responses contain custom headers', function () {
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('3961519424')
......@@ -105,7 +105,7 @@ describe('Responses contain custom headers', function () {
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('1487554813')
......@@ -132,7 +132,7 @@ describe('Responses contain custom headers', function () {
})
it('index.html has version', async function () {
const response = await request(app).get('/index.html')
const response = await request(app.server).get('/index.html')
expect(response.statusCode).to.equal(200)
// important here is, that it is different than in the test without meta.json
expect(response.headers.version).to.equal('319344871')
......
......@@ -37,7 +37,7 @@ describe('Responses contain custom headers', function () {
process.env.BUILD_TIMESTAMP = '0123456789'
process.env.CI_COMMIT_SHA = '0123456789abcdef'
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.contain({
id: 'ui-middleware',
......@@ -49,7 +49,7 @@ describe('Responses contain custom headers', function () {
})
it('has metadata from another ui service if available', async function () {
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.contain({
name: 'sample-service',
......@@ -58,7 +58,7 @@ describe('Responses contain custom headers', function () {
})
it('has updated metadata if config is updated', async function () {
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.body).to.have.length(2)
config.urls = []
......@@ -68,7 +68,7 @@ describe('Responses contain custom headers', function () {
await updateVersionProcessor()
})
const response2 = await request(app).get('/meta')
const response2 = await request(app.server).get('/meta')
expect(response2.body).to.have.length(1)
})
......@@ -79,7 +79,7 @@ describe('Responses contain custom headers', function () {
it('does not have metadata from ui service when unavailable', async function () {
await import('../src/cache.js').then(({ clear }) => clear())
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.statusCode).to.equal(200)
expect(response.body).to.not.deep.contain({
name: 'sample-service',
......@@ -105,7 +105,7 @@ describe('Responses contain custom headers', function () {
})
it('has metadata', async function () {
const response = await request(app).get('/meta')
const response = await request(app.server).get('/meta')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.contain({
name: 'sample-service',
......
......@@ -28,19 +28,19 @@ describe('Redirects', function () {
})
it('without requested location', async function () {
const response = await request(app).post('/redirect')
const response = await request(app.server).post('/redirect')
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('../busy.html')
})
it('with requested location', async function () {
const response = await request(app).post('/redirect').send('location=/appsuite/whatever/path')
const response = await request(app.server).post('/redirect').send('location=/appsuite/whatever/path')
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/appsuite/whatever/path')
})
it('redirects /ui to /', async function () {
const response = await request(app).get('/ui')
const response = await request(app.server).get('/ui')
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/')
})
......
......@@ -31,10 +31,10 @@ describe('Redis', function () {
it('use internal cache, when redis is disabled', async function () {
expect(spy.callCount).to.equal(0)
let response = await request(app).get('/example.js')
let response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
response = await request(app).get('/example.js')
response = await request(app.server).get('/example.js')
expect(response.statusCode).to.equal(200)
expect(spy.callCount).to.equal(1)
})
......
......@@ -23,19 +23,24 @@ describe('UI Middleware', function () {
afterEach(async function () {
td.reset()
await new RedisMock().flushdb()
process.env.CACHE_TTL = 30000
process.env.CACHE_TTL = '30000'
})
// Some say, this is not necessary to test
// But failing startups due to code errors in the probes will cause this to not work
// Therefore, we should keep this
it('is healthy', async function () {
const response = await request(app).get('/healthy')
it('is ready', async function () {
const response = await request(app.server).get('/ready')
expect(response.statusCode).to.equal(200)
})
it('is live', async function () {
const response = await request(app.server).get('/live')
expect(response.statusCode).to.equal(200)
})
it('fetches manifest data', async function () {
const response = await request(app).get('/manifests')
const response = await request(app.server).get('/manifests')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }])
})
......@@ -51,7 +56,7 @@ describe('UI Middleware', function () {
})
it('caches manifest data', async function () {
const response = await request(app).get('/manifests')
const response = await request(app.server).get('/manifests')
expect(response.statusCode).to.equal(200)
expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }])
......@@ -62,7 +67,7 @@ describe('UI Middleware', function () {
await new Promise(resolve => setTimeout(resolve, 150))
const response2 = await request(app).get('/manifests')
const response2 = await request(app.server).get('/manifests')
expect(response2.statusCode).to.equal(200)
expect(response2.body).to.deep.equal([{ namespace: 'test', path: 'example' }])
})
......@@ -86,7 +91,7 @@ describe('UI Middleware', function () {
})
it('can load multiple configurations', async function () {
await request(app)
await request(app.server)
.get('/manifests')
.then(response => {
expect(response.statusCode).to.equal(200)
......
......@@ -45,7 +45,7 @@ export function mockRedis (data = {}, isEnabled = true) {
const mock = {
isReady () { return Promise.resolve() },
isEnabled () { return isEnabled },
client: new RedisMock({ data }),
client: new RedisMock(data),
pubClient: new RedisMock(),
subClient: new RedisMock()
}
......@@ -56,7 +56,8 @@ export function mockRedis (data = {}, isEnabled = true) {
export async function mockApp () {
register.clear()
const { createApp } = await import('../src/create-app.js')
const app = createApp()
await request(app).get('/ready')
const app = await createApp()
app.listen({ port: 0 })
await request(app.server).get('/ready')
return app
}
......@@ -4,8 +4,9 @@ import { logger } from './logger.js'
class Config {
async load () {
const urlsSource = await fs.readFile('./config/config.yaml', 'utf8')
this._urls = yaml.load(urlsSource).baseUrls
const doc = yaml.load(await fs.readFile('./config/config.yaml', 'utf8'))
// @ts-ignore
this._urls = doc.baseUrls
logger.debug('[Config] Config has been loaded')
}
......
import express from 'express'
import helmet from 'helmet'
import { httpLogger } from './logger.js'
import { metricsMiddleware } from '@open-xchange/metrics'
import swaggerUi from 'swagger-ui-express'
import fastify from 'fastify'
import formBodyPlugin from '@fastify/formbody'
import fastifySwagger from '@fastify/swagger'
import fastifyMetrics from 'fastify-metrics'
import helmet from '@fastify/helmet'
import yaml from 'js-yaml'
import fs from 'fs'
import versionMiddleware from './middlewares/version.js'
import serveFilesMiddleware from './middlewares/serve-files.js'
import finalHandlerMiddleware from './middlewares/final-handler.js'
import health from './routes/health.js'
import manifestsRouter from './routes/manifests.js'
import metadataRouter from './routes/metadata.js'
import redirectsRouter from './routes/redirects.js'
import versionHandler from './handlers/version.js'
import healthPlugin from './plugins/health.js'
import manifestsPlugin from './plugins/manifests.js'
import metadataPlugin from './plugins/metadata.js'
import redirectsPlugin from './plugins/redirects.js'
import serveFilePlugin from './plugins/serve-files.js'
import { logger } from './logger.js'
const swaggerDocument = yaml.load(fs.readFileSync('./src/swagger.yaml', 'utf8'))
const metricsMiddlewareInstance = metricsMiddleware()
export function createApp () {
const app = express()
app.use(express.urlencoded({ extended: true }))
// Application-level middleware
app.use(httpLogger)
app.use(helmet({
export async function createApp (basePath) {
const app = fastify({
logger,
connectionTimeout: 30000
})
await app.register(formBodyPlugin)
await app.register(helmet, {
contentSecurityPolicy: false,
crossOriginEmbedderPolicy: false,
originAgentCluster: false,
crossOriginOpenerPolicy: { policy: 'same-origin-allow-popups' }
}))
app.use(health)
app.use(metricsMiddlewareInstance)
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument))
app.use('/swagger.json', (req, res) => res.json(swaggerDocument))
app.timeout = 30000
app.use(versionMiddleware)
app.use(manifestsRouter)
app.use(metadataRouter)
app.use(redirectsRouter)
app.use(serveFilesMiddleware)
app.use(finalHandlerMiddleware)
})
await app.register(healthPlugin)
await app.register(fastifyMetrics, {
routeMetrics: {
routeBlacklist: ['/ready', '/live', '/health', '/healthy', '/metrics', '/api-docs']
}
})
await app.register(fastifySwagger, {
routePrefix: '/api-docs',
prefix: process.env.APP_ROOT,
swagger: swaggerDocument,
exposeRoute: process.env.EXPOSE_API_DOCS === 'true'
})
await app.addHook('preHandler', versionHandler)
await app.register(manifestsPlugin, { prefix: process.env.APP_ROOT })
await app.register(metadataPlugin, { prefix: process.env.APP_ROOT })
await app.register(redirectsPlugin, { prefix: process.env.APP_ROOT })
await app.register(serveFilePlugin, { prefix: process.env.APP_ROOT })
return app
}
import { getLatestVersion } from '../version.js'
export default async function versionHandler (req, reply) {
const latestVersion = await getLatestVersion()
const version = req.headers.version || latestVersion
reply.header('version', version)
reply.header('latest-version', latestVersion)
reply.version = version
}
......@@ -3,18 +3,14 @@
import { config } from 'dotenv-defaults'
import { logger } from './logger.js'
import { createApp } from './create-app.js'
import express from 'express'
import createQueues from './create-queues.js'
config()
const root = express()
const app = createApp()
const app = await createApp()
createQueues()
// Binds and listens for connections on the specified host and port
root.listen(process.env.PORT, () => {
logger.info(`[Server] ui-middleware listening on port ${process.env.PORT}, PID: ${process.pid}`)
})
app.listen({ host: '::', port: Number(process.env.PORT) })
process.on('uncaughtException', err => {
logger.error(err, 'uncaughtException')
......@@ -25,5 +21,3 @@ process.on('unhandledRejection', err => {
logger.error(err, 'unhandledRejection')
process.exit(1)
})
root.use(process.env.APP_ROOT, app)
import { config } from 'dotenv-defaults'
import { createLogger, createHttpLogger } from '@open-xchange/logging'
import { createLogger } from '@open-xchange/logging'
config()
export const httpLogger = createHttpLogger()
export const logger = createLogger()
export const t0 = +new Date()
export const timeSinceStartup = () => (new Date() - t0) / 1000
export const t0 = Date.now()
export const timeSinceStartup = () => (Date.now() - t0) / 1000
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment