@pbvision/cloud-run-service 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +8 -0
- package/.github/workflows/check-pr.yml +1 -1
- package/.vscode/settings.json +2 -0
- package/Dockerfile +2 -2
- package/README.md +9 -7
- package/docs/cloudbuild/preview.example.yaml +1 -1
- package/docs/cloudbuild/release.example.yaml +1 -1
- package/package.json +5 -6
- package/src/app.js +3 -4
- package/src/main.js +16 -6
- package/src/placeholder.js +1 -1
- package/src/tasks.js +5 -4
- package/src/utils.js +12 -23
- package/test/main.js +4 -0
- package/test/unit-test-utils.js +4 -13
package/.env
ADDED
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
shell: bash
|
|
22
22
|
- name: 'Setup Firestore Emulator'
|
|
23
23
|
run: |
|
|
24
|
-
echo '{ "projects": { "default": "
|
|
24
|
+
echo '{ "projects": { "default": "xyz-dev" } }' > ./.firebaserc
|
|
25
25
|
echo '{ "firestore": {} }' > ./firebase.json
|
|
26
26
|
npm install -g firebase-tools
|
|
27
27
|
firebase setup:emulators:firestore
|
package/.vscode/settings.json
CHANGED
package/Dockerfile
CHANGED
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
|
13
13
|
- Must run with a service account named
|
|
14
|
-
`cr-${process.env.SERVICE}@${process.env.
|
|
14
|
+
`cr-${process.env.SERVICE}@${process.env.GCLOUD_PROJECT}.iam.gserviceaccount.com`
|
|
15
15
|
with at least these permissions (along with whatever else your service
|
|
16
16
|
requires):
|
|
17
17
|
- `roles/run.invoker` - if the service needs to call other services
|
|
@@ -22,15 +22,17 @@
|
|
|
22
22
|
|
|
23
23
|
- Environment variables:
|
|
24
24
|
- Required:
|
|
25
|
+
- `GCLOUD_PROJECT` - the project ID this service is running in; must end in
|
|
26
|
+
`-dev` or `-prod`
|
|
25
27
|
- `GIT_HASH` - the commit from which the current code was generated
|
|
26
28
|
- `K_REVISION` - provided by cloud run (the revision ID)
|
|
27
|
-
-
|
|
29
|
+
- When running a local server, it will be `localhost`
|
|
30
|
+
- When running it unit tests, it should be`unittest`
|
|
31
|
+
- If this is `unittest` then `isUnitTesting` will be true.
|
|
28
32
|
- `NODE_ENV`
|
|
29
|
-
- If this is `localhost` then `isLocalhost
|
|
30
|
-
- If this is `dev` then `isDev
|
|
31
|
-
- If this is `prod` then `isProd
|
|
32
|
-
- `PROJECT` - the project ID this service is running in
|
|
33
|
-
- Other endings are not allowed.
|
|
33
|
+
- If this is `localhost` then `isLocalhost` will be true.
|
|
34
|
+
- If this is `dev` then `isDev` will be true.
|
|
35
|
+
- If this is `prod` then `isProd` will be true.
|
|
34
36
|
- `REGION` - the region this service's cloud run is located in
|
|
35
37
|
- `SERVICE` - the name of this service
|
|
36
38
|
- Optional:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pbvision/cloud-run-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "fastify-firestore-service Web Framework on Cloud Run",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -10,14 +10,13 @@
|
|
|
10
10
|
"exports": "./src/index.js",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"coverage": "yarn -s start-local-db && yarn -s test --coverage",
|
|
13
|
-
"debug": "
|
|
13
|
+
"debug": "./node_modules/nodemon/bin/nodemon.js --no-lazy --legacy-watch --watch ./src --watch ./test --inspect=9229 node --experimental-vm-modules --exec yarn test --runInBand",
|
|
14
14
|
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
|
15
15
|
"setup": "yarn install --frozen-lockfile",
|
|
16
|
-
"start-docker": "wd=`pwd`; tag=`basename $wd`; docker build --build-arg PROJECT=xyz-dev --build-arg GIT_HASH=`git rev-parse HEAD` . --tag $tag && docker run -p 127.0.0.1:8080:8080/tcp --env REGION='us-central1' --env PORT=8080 --env K_REVISION=localhost --env SERVICE=tbd $tag:latest && docker run $tag:latest",
|
|
17
16
|
"start-local-db": "./node_modules/@pbvision/firestore-orm/scripts/start-local-db.sh",
|
|
17
|
+
"start-local": "yarn -s start-local-db && K_REVISION=localhost node --env-file=.env test/main.js",
|
|
18
18
|
"test": "yarn -s start-local-db && yarn -s test-without-starting-db",
|
|
19
|
-
"test-without-starting-db": "
|
|
20
|
-
"start-local": "yarn -s start-local-db && FIRESTORE_EMULATOR_HOST=[::1]:8404 NODE_ENV=localhost PROJECT=xyz-dev REGION=us-central1 PORT=8080 K_REVISION=localhost PROJECT=xyz-dev GIT_HASH=`git rev-parse HEAD` node src/main.js",
|
|
19
|
+
"test-without-starting-db": "node --env-file=.env --experimental-vm-modules ./node_modules/jest/bin/jest.js --config=./jest.config.json",
|
|
21
20
|
"watch": "yarn -s test --watch"
|
|
22
21
|
},
|
|
23
22
|
"repository": {
|
|
@@ -29,7 +28,7 @@
|
|
|
29
28
|
},
|
|
30
29
|
"dependencies": {
|
|
31
30
|
"@google-cloud/tasks": "^4.0.1",
|
|
32
|
-
"@pbvision/fastify-firestore-service": "^0.0.
|
|
31
|
+
"@pbvision/fastify-firestore-service": "^0.0.25",
|
|
33
32
|
"google-auth-library": "^9.4.2"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
package/src/app.js
CHANGED
|
@@ -4,7 +4,6 @@ import { port } from './port.js'
|
|
|
4
4
|
import { isProd } from './utils.js'
|
|
5
5
|
|
|
6
6
|
export async function makePBVService (components, customizePinoOpts) {
|
|
7
|
-
const isProdEnv = isProd()
|
|
8
7
|
return makeService({
|
|
9
8
|
service: process.env.SERVICE,
|
|
10
9
|
components,
|
|
@@ -15,16 +14,16 @@ export async function makePBVService (components, customizePinoOpts) {
|
|
|
15
14
|
path: '/_healthcheck'
|
|
16
15
|
},
|
|
17
16
|
latencyTracker: {
|
|
18
|
-
disabled:
|
|
17
|
+
disabled: isProd
|
|
19
18
|
},
|
|
20
19
|
logging: {
|
|
21
20
|
customizePinoOpts,
|
|
22
|
-
reportErrorDetail: !
|
|
21
|
+
reportErrorDetail: !isProd,
|
|
23
22
|
reportAllErrors: true,
|
|
24
23
|
sentryDSN: process.env.sentryDSN
|
|
25
24
|
},
|
|
26
25
|
swagger: {
|
|
27
|
-
disabled:
|
|
26
|
+
disabled: isProd,
|
|
28
27
|
servers: [`http://localhost:${port}`],
|
|
29
28
|
routePrefix: '/app/docs'
|
|
30
29
|
}
|
package/src/main.js
CHANGED
|
@@ -5,15 +5,25 @@ import { API } from '@pbvision/fastify-firestore-service'
|
|
|
5
5
|
import { makePBVService } from './app.js'
|
|
6
6
|
import { callServiceAPI } from './call-service-api.js'
|
|
7
7
|
import { port } from './port.js'
|
|
8
|
+
import { isLocalhost } from './utils.js'
|
|
8
9
|
|
|
9
10
|
API.prototype.callServiceAPI = callServiceAPI
|
|
10
11
|
|
|
11
12
|
let service
|
|
12
|
-
const project = process.env.
|
|
13
|
+
const project = process.env.GCLOUD_PROJECT
|
|
13
14
|
|
|
14
15
|
function verifyEnvironmentVariables () {
|
|
15
16
|
const requiredEnvKeys = [
|
|
16
|
-
'
|
|
17
|
+
'GCLOUD_PROJECT', 'K_REVISION', 'NODE_ENV', 'REGION', 'SERVICE']
|
|
18
|
+
// istanbul ignore else
|
|
19
|
+
if (isLocalhost) {
|
|
20
|
+
assert(process.env.FIRESTORE_EMULATOR_HOST, 'use emulator on localhost')
|
|
21
|
+
assert(['localhost', 'unittest'].indexOf(process.env.K_REVISION) !== -1,
|
|
22
|
+
'K_REVISION must be "localhost" or "unittest" in this environment')
|
|
23
|
+
} else {
|
|
24
|
+
// must have GIT_HASH to send to sentry in the cloud
|
|
25
|
+
requiredEnvKeys.push('GIT_HASH')
|
|
26
|
+
}
|
|
17
27
|
for (const k of requiredEnvKeys) {
|
|
18
28
|
assert(process.env[k], `${k} environment variable must be set`)
|
|
19
29
|
}
|
|
@@ -57,7 +67,7 @@ export async function makeService (components) {
|
|
|
57
67
|
|
|
58
68
|
// istanbul ignore next
|
|
59
69
|
export async function runService (components) {
|
|
60
|
-
if (
|
|
70
|
+
if (!isLocalhost) {
|
|
61
71
|
// if the instance tells us it will shutdown, try to shut down gracefully (for
|
|
62
72
|
// example flushing logs)
|
|
63
73
|
process.on('SIGTERM', () => {
|
|
@@ -75,8 +85,8 @@ export async function runService (components) {
|
|
|
75
85
|
console.log('an error happened', err)
|
|
76
86
|
}), 7000)
|
|
77
87
|
})
|
|
78
|
-
// start the server
|
|
79
|
-
service = await makeService(components)
|
|
80
|
-
service.listen({ port, host: '0.0.0.0' })
|
|
81
88
|
}
|
|
89
|
+
// start the server
|
|
90
|
+
service = await makeService(components)
|
|
91
|
+
service.listen({ port, host: '0.0.0.0' })
|
|
82
92
|
}
|
package/src/placeholder.js
CHANGED
|
@@ -35,7 +35,7 @@ export class TestCallServiceAPI extends API {
|
|
|
35
35
|
static RESPONSE = { code: S.int, body: S.str }
|
|
36
36
|
|
|
37
37
|
async computeResponse () {
|
|
38
|
-
assert(isUnitTesting
|
|
38
|
+
assert(isUnitTesting)
|
|
39
39
|
const resp = await this.callServiceAPI(this.req.body)
|
|
40
40
|
return {
|
|
41
41
|
code: resp.code,
|
package/src/tasks.js
CHANGED
|
@@ -28,8 +28,9 @@ export async function enqueueCloudTask ({
|
|
|
28
28
|
service = 'internal',
|
|
29
29
|
name = undefined, ignoreNameAlreadyUsedError = false
|
|
30
30
|
}) {
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const project = process.env.GCLOUD_PROJECT
|
|
32
|
+
const region = process.env.REGION
|
|
33
|
+
const parent = tasksClient.queuePath(project, region, queue)
|
|
33
34
|
const protocolAndHost = getServiceProtocolAndHost(service)
|
|
34
35
|
const task = {
|
|
35
36
|
httpRequest: {
|
|
@@ -40,12 +41,12 @@ export async function enqueueCloudTask ({
|
|
|
40
41
|
url: `${protocolAndHost}/${queue.replace(/-/g, '_')}`,
|
|
41
42
|
body: Buffer.from(JSON.stringify(payload)).toString('base64'),
|
|
42
43
|
oidcToken: {
|
|
43
|
-
serviceAccountEmail: `cr-${process.env.SERVICE}@${
|
|
44
|
+
serviceAccountEmail: `cr-${process.env.SERVICE}@${project}.iam.gserviceaccount.com`
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
if (name) {
|
|
48
|
-
const fqName = tasksClient.taskPath(
|
|
49
|
+
const fqName = tasksClient.taskPath(project, region, queue, name)
|
|
49
50
|
task.name = fqName
|
|
50
51
|
}
|
|
51
52
|
const request = { parent, task }
|
package/src/utils.js
CHANGED
|
@@ -2,6 +2,15 @@ import assert from 'node:assert'
|
|
|
2
2
|
|
|
3
3
|
import { port as portForThisService } from './port.js'
|
|
4
4
|
|
|
5
|
+
export const isLocalhost = process.env.NODE_ENV === 'localhost'
|
|
6
|
+
export const isDev = process.env.NODE_ENV === 'dev'
|
|
7
|
+
export const isProd = process.env.NODE_ENV === 'prod'
|
|
8
|
+
// istanbul ignore next
|
|
9
|
+
assert(isLocalhost || isDev || isProd, 'must be on localhost, dev or prod')
|
|
10
|
+
|
|
11
|
+
export const isUnitTesting = process.env.K_REVISION === 'unittest'
|
|
12
|
+
assert(!isUnitTesting || isLocalhost, 'must be on localhost if unit testing')
|
|
13
|
+
|
|
5
14
|
export function getServiceProtocolAndHost (serviceName) {
|
|
6
15
|
const host = getServiceHost(serviceName)
|
|
7
16
|
// istanbul ignore next
|
|
@@ -10,11 +19,12 @@ export function getServiceProtocolAndHost (serviceName) {
|
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
export function getServiceHost (serviceName) {
|
|
13
|
-
|
|
22
|
+
// manually check NODE_ENV for testing purposes
|
|
23
|
+
if (process.env.NODE_ENV === 'localhost') {
|
|
14
24
|
if (process.env.SERVICE === serviceName) {
|
|
15
25
|
return `localhost:${portForThisService}`
|
|
16
26
|
}
|
|
17
|
-
const portMapping = JSON.parse(process.env.LOCAL_SERVICE_PORT_MAP
|
|
27
|
+
const portMapping = JSON.parse(process.env.LOCAL_SERVICE_PORT_MAP)
|
|
18
28
|
const port = portMapping[serviceName]
|
|
19
29
|
assert(port, `unknown service or missing port for localhost ${serviceName}`)
|
|
20
30
|
return `localhost:${port}`
|
|
@@ -23,25 +33,4 @@ export function getServiceHost (serviceName) {
|
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
35
|
|
|
26
|
-
export function isLocalhost () {
|
|
27
|
-
assert(process.env.NODE_ENV)
|
|
28
|
-
return process.env.NODE_ENV === 'localhost'
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isUnitTesting () {
|
|
32
|
-
const ret = process.env.K_REVISION === 'unittest'
|
|
33
|
-
assert(!ret || isLocalhost(), 'must on localhost if in unit tests')
|
|
34
|
-
return ret
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function isProd () {
|
|
38
|
-
assert(process.env.NODE_ENV)
|
|
39
|
-
return process.env.NODE_ENV === 'prod'
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function isDev () {
|
|
43
|
-
assert(process.env.NODE_ENV)
|
|
44
|
-
return process.env.NODE_ENV === 'dev'
|
|
45
|
-
}
|
|
46
|
-
|
|
47
36
|
export const now = () => Math.floor(new Date().getTime() / 1000)
|
package/test/main.js
ADDED
package/test/unit-test-utils.js
CHANGED
|
@@ -17,21 +17,12 @@ class TestUtils extends BaseTest {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
testEnv () {
|
|
20
|
+
expect(process.env.GIT_HASH).toBe(undefined)
|
|
20
21
|
expect(process.env.K_REVISION).toBe('unittest')
|
|
21
22
|
expect(process.env.NODE_ENV).toBe('localhost')
|
|
22
|
-
expect(isLocalhost
|
|
23
|
-
expect(isProd
|
|
24
|
-
expect(isDev
|
|
25
|
-
|
|
26
|
-
process.env.NODE_ENV = 'dev'
|
|
27
|
-
expect(isLocalhost()).toBe(false)
|
|
28
|
-
expect(isProd()).toBe(false)
|
|
29
|
-
expect(isDev()).toBe(true)
|
|
30
|
-
|
|
31
|
-
process.env.NODE_ENV = 'prod'
|
|
32
|
-
expect(isLocalhost()).toBe(false)
|
|
33
|
-
expect(isProd()).toBe(true)
|
|
34
|
-
expect(isDev()).toBe(false)
|
|
23
|
+
expect(isLocalhost).toBe(true)
|
|
24
|
+
expect(isProd).toBe(false)
|
|
25
|
+
expect(isDev).toBe(false)
|
|
35
26
|
}
|
|
36
27
|
|
|
37
28
|
testGetServiceHost () {
|