Clean docker and migrate (#189)

* feat: autoload migrations and simplify start script

no need to get the migrations from the migration files from the repo and to mount a specific volume
to the Hasura GE service. By default, when HBP starts, it checks the migrations and installs it if
required. This can be disabled by setting AUTO_MIGRATE to false. This system could be extended to
set AUTO_MIGRATE=v1 so it runs another set of migrations that would transform schema and data from
HBP v1 to HBP v2

* fix: include mock migrations used for testing

* fix: run migrations in a separate node script

* refactor: better general repo structure, and migration that works

* ci: change test mock migration folder in GH actions
This commit is contained in:
Pilou
2020-04-30 13:10:12 +00:00
committed by GitHub
parent ba2f36cabf
commit 29b4d913d4
43 changed files with 348 additions and 246 deletions

View File

@@ -16,3 +16,4 @@ jest*
npm-debug.log
yarn-error.log
**/*.test.ts
src/test

View File

@@ -44,4 +44,3 @@ SMTP_PASS=password
SMTP_USER=user
SMTP_SECURE=false
SMTP_SENDER=hbp@hbp.com
NOTIFY_EMAIL_CHANGE=true

View File

@@ -1,23 +0,0 @@
# Environment variables that will be loaded in the docker-compose.dev hbp service
# It should contain secrets used in Jest and that SHOULD NEVER BE INCLUDED IN GIT!!!
# OAuth Providers ids and secrets
GITHUB_CLIENT_ID=an_oauth_client_id
GITHUB_CLIENT_SECRET=the_given_secret_string
GOOGLE_CLIENT_ID=an_oauth_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=the_given_secret_string
LINKEDIN_CLIENT_ID=an_oauth_client_id
LINKEDIN_CLIENT_SECRET=the_given_secret_string
# Credentials to test against the providers
TEST_GITHUB_USERNAME=bobthetester@gmail.com
TEST_GITHUB_PASSWORD=one.two.three.four.is.not.a.good.passord.even.for.a.test.account
TEST_GOOGLE_USERNAME=bobthetester@gmail.com
TEST_GOOGLE_PASSWORD=one.two.three.four.is.not.a.good.passord.even.for.a.test.account
TEST_LINKEDIN_USERNAME=bobthetester@gmail.com
TEST_LINKEDIN_PASSWORD=one.two.three.four.is.not.a.good.passord.even.for.a.test.account

View File

@@ -54,6 +54,7 @@ jobs:
container:
image: node:12
env:
NODE_ENV: CI
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
HASURA_GRAPHQL_ADMIN_SECRET: ${{ env.HASURA_GRAPHQL_ADMIN_SECRET }}
HASURA_ENDPOINT: http://graphql-engine:8080/v1/graphql
@@ -88,14 +89,8 @@ jobs:
&& apt-get update
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf --no-install-recommends
&& rm -rf /var/lib/apt/lists/*
- name: Install Hasura CLI
run: yarn global add hasura-cli
- name: Create Hasura CLI config
run: "printf 'endpoint: http://graphql-engine:8080\nHASURA_GRAPHQL_ADMIN_SECRET: ${{ env.HASURA_GRAPHQL_ADMIN_SECRET }}\n' > config.yaml"
- name: Copy mock migrations
run: cp -r mock-config/migrations/* migrations/
- name: Run migrations
run: hasura migrate apply
- name: Copy test migrations
run: cp -r test-mocks/migrations/* migrations/
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
dist
.env*
!.env.test
!.env.ci
!.env*.example
*.log
hasura

View File

@@ -1,18 +1,5 @@
FROM node:10-slim
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer installs, work.
RUN apt-get update \
&& apt-get install -y wget gnupg git \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ARG NODE_ENV=development
ENV NODE_ENV $NODE_ENV
ENV PORT 3000
@@ -24,6 +11,4 @@ RUN yarn install
COPY . .
HEALTHCHECK --interval=5s --timeout=5s --retries=3 CMD wget localhost:${PORT}/healthz -q -O - > /dev/null 2>&1
CMD ["yarn", "run", "dev-container"]
CMD ["yarn", "run", "dev:docker"]

27
Dockerfile.test Normal file
View File

@@ -0,0 +1,27 @@
FROM node:10-slim
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer installs, work.
RUN apt-get update \
&& apt-get install -y wget gnupg git \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ARG NODE_ENV=development
ENV NODE_ENV $NODE_ENV
ENV PORT 3000
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
CMD ["yarn", "run", "test"]

View File

@@ -1,17 +0,0 @@
#!/bin/bash
export-dotenv() { # export-env <dotenv-file> <var-name> <original-var-name(optional)>
value=$(cat $1 | sed 's/#.*//g' | grep -o "^${2}.*" | xargs)
value=$(echo "${value#*=}")
var_name=${2:-3}
initial_value=$(printf '%s' "${!var_name}")
export $(echo ${var_name})=${value:-$initial_value}
}
wait-for() { # wait-for <url> <service-name(optional)>
printf "Waiting for the service ${2:-1} to be ready..."
until $(curl -X GET --output /dev/null --silent --head --fail ${1}); do
printf '.'
sleep 1
done
echo
}

View File

@@ -4,10 +4,11 @@ services:
build:
context: .
dockerfile: Dockerfile.dev
command: '${COMMAND_LINE:-yarn run dev-container}'
env_file: .env.dockerdev
env_file: '.env.development'
depends_on:
- 'mailhog'
environment:
NODE_ENV: '${NODE_ENV:-test}'
NODE_ENV: development
JWT_ALGORITHM: HS256
JWT_KEY: '${JWT_KEY}'
SMTP_HOST: mailhog
@@ -16,14 +17,11 @@ services:
volumes:
- .:/app
- /app/node_modules
tty: true
graphql-engine:
ports:
- '8080:8080'
environment:
HASURA_GRAPHQL_JWT_SECRET: '{"type": "HS256", "key": "${JWT_KEY}"}'
volumes:
- ./mock-config/migrations/1585679214182_custom_user_column:/hasura-migrations/1585679214182_custom_user_column
minio:
ports:
- 8000:9000 # Do not use port 9000 in the host machine as developpers using portainer might already use it

25
docker-compose.test.yaml Normal file
View File

@@ -0,0 +1,25 @@
version: '3.6'
services:
hasura-backend-plus:
build:
context: .
dockerfile: Dockerfile.test
env_file: '.env.test'
depends_on:
- 'mailhog'
environment:
NODE_ENV: test
JWT_ALGORITHM: HS256
JWT_KEY: '${JWT_KEY}'
SMTP_HOST: mailhog
SMTP_PORT: 1025
SMTP_SECURE: 'false'
volumes:
- .:/app
- /app/node_modules
- ./test-mocks/migrations/1585679214182_custom_user_column:/app/migrations/1585679214182_custom_user_column
graphql-engine:
environment:
HASURA_GRAPHQL_JWT_SECRET: '{"type": "HS256", "key": "${JWT_KEY}"}'
mailhog:
image: mailhog/mailhog

View File

@@ -8,7 +8,7 @@ services:
environment:
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-postgrespassword}'
graphql-engine:
image: hasura/graphql-engine:v1.1.0.cli-migrations
image: hasura/graphql-engine:v1.1.0
depends_on:
- 'postgres'
restart: always
@@ -16,14 +16,14 @@ services:
HASURA_GRAPHQL_ADMIN_SECRET: '${HASURA_GRAPHQL_ADMIN_SECRET:?HASURA_GRAPHQL_ADMIN_SECRET}'
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-postgrespassword}@postgres:5432/postgres
HASURA_GRAPHQL_JWT_SECRET: '{"type": "RS256", "jwk_url": "http://hasura-backend-plus:3000/auth/jwks"}'
volumes:
- ./migrations:/hasura-migrations
hasura-backend-plus:
image: nhost/hasura-backend-plus:latest
depends_on:
- 'graphql-engine'
- 'minio'
ports:
- '3000:${PORT:-3000}'
- '3000:3000'
environment:
PORT: '${PORT:-3000}'
HASURA_GRAPHQL_ADMIN_SECRET: '${HASURA_GRAPHQL_ADMIN_SECRET:?HASURA_GRAPHQL_ADMIN_SECRET}'
HASURA_ENDPOINT: http://graphql-engine:8080/v1/graphql
S3_ENDPOINT: http://minio:9000

View File

@@ -1,10 +1,11 @@
module.exports = {
globalSetup: './jest/setup.js',
globalTeardown: './jest/teardown.js',
testEnvironment: './jest/puppeteer_environment.js',
globalSetup: './src/test/setup.ts',
globalTeardown: './src/test/teardown.ts',
testEnvironment: './src/test/puppeteer_environment.js',
verbose: true,
moduleNameMapper: {
'^@shared/(.*)$': '<rootDir>/src/shared/$1'
'^@shared/(.*)$': '<rootDir>/src/shared/$1',
'^@test/(.*)$': '<rootDir>/src/test/$1'
},
testPathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/node_modules/'],
setupFilesAfterEnv: ['expect-puppeteer', 'jest-extended'],

View File

@@ -1,8 +0,0 @@
function getPuppeteer() {
const puppeteer = require('puppeteer-extra')
const StealthPlugin = require('puppeteer-extra-plugin-stealth')
puppeteer.use(StealthPlugin())
return puppeteer
}
module.exports.getPuppeteer = getPuppeteer

View File

@@ -1,7 +0,0 @@
// const { teardown: teardownServer } = require('jest-dev-server')
module.exports = async function teardown(jestConfig = {}) {
// if (!jestConfig.watch && !jestConfig.watchAll) {
// await teardownServer()
// }
}

View File

@@ -9,7 +9,7 @@
"main": "src/start.ts",
"scripts": {
"dev": "ts-node-dev -r tsconfig-paths/register --no-notify src/start.ts",
"dev-container": "ts-node-dev -r tsconfig-paths/register --no-deps --respawn --poll --interval 1000 --no-notify src/start.ts",
"dev:docker": "ts-node-dev -r tsconfig-paths/register --no-deps --respawn --poll --interval 1000 --no-notify src/start.ts",
"docs:build": "vuepress build docs",
"docs:dev": "vuepress dev docs",
"start": "ts-node -r tsconfig-paths/register src/start.ts",
@@ -69,6 +69,7 @@
"graphql": "15.0.0",
"graphql-request": "1.8.2",
"graphql-tag": "2.10.3",
"hasura-cli": "^1.1.1",
"helmet": "3.22.0",
"hibp": "9.0.0",
"jose": "1.26.0",

70
src/migrate.ts Normal file
View File

@@ -0,0 +1,70 @@
import { spawnSync } from 'child_process'
import fetch from 'node-fetch'
import { writeFileSync } from 'fs'
import url from 'url'
import cors from 'cors'
import express from 'express'
import helmet from 'helmet'
import { HASURA_ENDPOINT, HASURA_GRAPHQL_ADMIN_SECRET, AUTO_MIGRATE, PORT } from '@shared/config'
import getJwks from './routes/auth/jwks'
const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))
const waitFor = async (path: string, attempts = 240): Promise<void> => {
if (attempts > 0) {
try {
if ((await fetch(path)).status !== 200) {
await sleep(1000)
await waitFor(path, attempts--)
}
} catch (error) {
await sleep(1000)
await waitFor(path, attempts--)
}
} else throw Error(`Unable to reach ${path}`)
}
export default async (): Promise<void> => {
if (AUTO_MIGRATE) {
console.log('Applying migrations...')
await new Promise((resolve, reject) => {
const app = express()
app.use(helmet())
app.use(cors())
app.get('/auth/jwks', getJwks)
/**
* ! See: https://github.com/hasura/graphql-engine/issues/3636
* ! When Hasura is set to use jwk_url with HBP, it needs to get the JWKS from HBP to start.
* ! As we need Hasura to be up to run the migrations, we provide a temporary server with only the JWKS endpoint.
*/
try {
const server = app.listen(PORT, async () => {
const { protocol, host } = url.parse(HASURA_ENDPOINT)
const hasuraURL = `${protocol}//${host}`
// * Wait for GraphQL Engine to be ready
console.log('Waiting for Hasura to be ready...')
await waitFor(`${hasuraURL}/healthz`)
// * Set the Hasura CLI config.yaml file
writeFileSync(
'config.yaml',
`endpoint: ${hasuraURL}\nadmin_secret: ${HASURA_GRAPHQL_ADMIN_SECRET}\n`,
{ encoding: 'utf8' }
)
// * Apply migrations
const { error, stdout } = spawnSync('./node_modules/.bin/hasura', [
'migrate',
'apply',
'--skip-update-check'
])
if (error) reject(error)
console.log(stdout.toString())
server.close()
})
server.on('close', () => resolve())
} catch (err) {
reject(err)
}
})
}
}

View File

@@ -8,14 +8,15 @@ import {
HIBP_ENABLE,
SMTP_ENABLE,
REDIRECT_URL_ERROR,
JWT_CLAIMS_NAMESPACE
JWT_CLAIMS_NAMESPACE,
PORT
} from '@shared/config'
import { generateRandomString, selectAccountByEmail } from '@shared/helpers'
import { deleteMailHogEmail, mailHogSearch } from '@shared/test-utils'
import { deleteMailHogEmail, mailHogSearch } from '@test/test-utils'
import { JWT } from 'jose'
import { Token } from '@shared/types'
import { server } from '../../start'
import { app } from '../../server'
import request from 'supertest'
/**
@@ -32,11 +33,11 @@ const password = generateRandomString()
/**
* Create agent for global state.
*/
const server = app.listen(PORT)
const agent = request(server)
// * Code that is executed after any jest test file that imports test-utiles
afterAll(async () => {
await server.close()
server.close()
})
it('should create an account', async () => {

View File

@@ -1,9 +1,9 @@
import 'jest-extended'
import { generateRandomString } from '@shared/helpers'
import { account, request } from '@shared/test-mock-account'
import { account, request } from '@test/test-mock-account'
import { mailHogSearch, deleteMailHogEmail } from '@shared/test-utils'
import { mailHogSearch, deleteMailHogEmail } from '@test/test-utils'
import { JWT } from 'jose'
import { Token } from '@shared/types'
import { JWT_CLAIMS_NAMESPACE, NOTIFY_EMAIL_CHANGE, SMTP_ENABLE } from '@shared/config'
@@ -51,9 +51,7 @@ it('should reconnect using the new email', async () => {
})
it('should receive an email notifying the email account has been changed', async () => {
console.log(SMTP_ENABLE, NOTIFY_EMAIL_CHANGE)
if (SMTP_ENABLE && NOTIFY_EMAIL_CHANGE) {
console.log('HERE')
const [message] = await mailHogSearch(account.email)
expect(message).toBeTruthy()
expect(message.Content.Headers.Subject).toInclude(

View File

@@ -1,9 +1,8 @@
import 'jest-extended'
import { generateRandomString } from '@shared/helpers'
import { account, request } from '@shared/test-mock-account'
import { mailHogSearch, deleteMailHogEmail } from '@shared/test-utils'
import { account, request } from '@test/test-mock-account'
import { mailHogSearch, deleteMailHogEmail } from '@test/test-utils'
let ticket: string

View File

@@ -1,6 +1,6 @@
import 'jest-extended'
import { account, request } from '@shared/test-mock-account'
import { account, request } from '@test/test-mock-account'
import { authenticator } from 'otplib'

View File

@@ -1,10 +1,9 @@
/* eslint-disable jest/no-standalone-expect */
import 'jest-extended'
// import { initAgent } from '@shared/test-utils'
import { SERVER_URL, PROVIDER_SUCCESS_REDIRECT, PROVIDERS } from '@shared/config'
import { SERVER_URL, PROVIDER_SUCCESS_REDIRECT, PROVIDERS, PORT } from '@shared/config'
import { agent } from 'supertest'
import { server } from '../../../start'
import { itif } from '@shared/test-utils'
import { app } from '../../../server'
import { itif } from '@test/test-utils'
// it('Oauth routes should not exist when disabled', async () => {
// const tempAgent = initAgent({ PROVIDERS: {} })
@@ -21,11 +20,11 @@ import { itif } from '@shared/test-utils'
// })
// TODO test functions in ./utils.ts
agent(server) // * Create the SuperTest agent
const server = app.listen(PORT)
agent(server)
// * Code that is executed after any jest test file that imports test-utiles
afterAll(async () => {
await server.close()
server.close()
})
itif(

View File

@@ -1,6 +1,6 @@
import 'jest-extended'
import { account, request } from '@shared/test-mock-account'
import { account, request } from '@test/test-mock-account'
it('should refresh the token', async () => {
const { body, status } = await request.post('/auth/token/refresh')

View File

@@ -1,6 +1,6 @@
import 'jest-extended'
import { account, getUserId, request } from '@shared/test-mock-account'
import { account, getUserId, request } from '@test/test-mock-account'
import fs from 'fs'
import { promisify } from 'util'

View File

@@ -11,7 +11,7 @@ export const {
} = process.env
export const PORT = castIntEnv('PORT', 3000)
export const HASURA_ENDPOINT = process.env.HASURA_ENDPOINT as string
export const AUTO_MIGRATE = castBooleanEnv('AUTO_MIGRATE', true)
/**
* * Rate limiter settings
*/

View File

@@ -1,6 +1,5 @@
import { PORT } from '@shared/config'
import { app } from './server'
import migrate from './migrate'
export const server = app.listen(PORT, () => {
console.log(`Running on http://localhost:${PORT}`)
})
migrate().then(() => app.listen(PORT, () => console.log(`Running on http://localhost:${PORT}`)))

8
src/test/getPuppeteer.ts Normal file
View File

@@ -0,0 +1,8 @@
require('tsconfig-paths/register')
import puppeteer, { PuppeteerExtra } from 'puppeteer-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
export const getPuppeteer = (): PuppeteerExtra => {
puppeteer.use(StealthPlugin())
return puppeteer
}

View File

@@ -0,0 +1,10 @@
{
"launch": {
"headless": true,
"executablePath": "google-chrome-unstable",
"args": [
"--no-sandbox",
"--disable-setuid-sandbox"
]
}
}

View File

@@ -3,7 +3,7 @@
const NodeEnvironment = require('jest-environment-node')
const chalk = require('chalk')
const { getPuppeteer } = require('./getPuppeteer')
const config = require('./puppeteer.config.js')
const config = require('./puppeteer.config.json')
const handleError = (error) => {
process.emit('uncaughtException', error)

View File

@@ -1,12 +1,20 @@
// setup.js
const config = require('./puppeteer.config.js')
const { getPuppeteer } = require('./getPuppeteer.js')
require('tsconfig-paths/register')
import config from './puppeteer.config.json'
import { getPuppeteer } from './getPuppeteer'
let didAlreadyRunInWatchMode = false
let browser
import { Config } from '@jest/types'
import migrate from '../migrate'
module.exports = async function (jestConfig = {}) {
export default async (jestConfig: Config.InitialOptions = {}): Promise<void> => {
console.log()
await migrate()
const puppeteer = getPuppeteer()
try {
browser = await puppeteer.launch(config.launch)
} catch {
browser = await puppeteer.launch({ ...config.launch, executablePath: undefined })
}
process.env.PUPPETEER_WS_ENDPOINT = browser.wsEndpoint()
// If we are in watch mode, - only setupServer() once.

10
src/test/teardown.ts Normal file
View File

@@ -0,0 +1,10 @@
require('tsconfig-paths/register')
// const { teardown: teardownServer } = require('jest-dev-server')
import { Config } from '@jest/types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default async (_jestConfig: Config.InitialOptions = {}): Promise<void> => {
// if (!jestConfig.watch && !jestConfig.watchAll) {
// await teardownServer()
// }
}

View File

@@ -1,16 +1,17 @@
import { SuperTest, Test, agent } from 'supertest'
import { TestAccount, createAccount, deleteEmailsOfAccount } from '@shared/test-utils'
import { TestAccount, createAccount, deleteEmailsOfAccount } from '@test/test-utils'
import { AUTO_ACTIVATE_NEW_USERS } from '@shared/config'
import { getClaims } from './jwt'
import { server } from '../start'
import { selectAccountByEmail } from './helpers'
import { AUTO_ACTIVATE_NEW_USERS, PORT } from '@shared/config'
import { getClaims } from '../shared/jwt'
import { app } from '../server'
import { selectAccountByEmail } from '../shared/helpers'
export let request: SuperTest<Test>
export let account: TestAccount
export const getUserId = (): string => getClaims(account.token)['x-hasura-user-id']
const server = app.listen(PORT)
// * Code that is executed before any jest test file that imports this file
beforeAll(async () => {
@@ -40,5 +41,5 @@ afterAll(async () => {
await request.post('/auth/account/delete').set('Authorization', `Bearer ${account.token}`)
// * Remove any message sent to this account
await deleteEmailsOfAccount(account.email)
await server.close()
server.close()
})

102
start.sh Executable file
View File

@@ -0,0 +1,102 @@
#!/bin/bash
# source bash-utils.sh
function help() {
echo "Usage:"
printf "\t${0} <command> [flags]\n"
echo "Flags:"
printf "\t-h, --help\t\tShow help\n"
printf "\t-b, --build\t\tForce build the docker file\n"
printf "\t-r, --remove-volumes\t\Remove all volumes after shuting down\n"
echo "Commands:"
printf "\tdev\t Development without runnig any test\n"
printf "\twatch\t Development with Jest watching\n"
printf "\ttest\t Run Jest tests\n"
exit
}
# * Get script arguments
while :; do
case $1 in
-h|-\?|--help)
help $0
;;
-b|--build)
build="yes"
;;
-r|--remove-volumes)
remove="-v"
;;
--)
shift
break
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
test)
mode="test"
remove='-v'
;;
dev)
mode="development"
;;
watch)
mode="test"
COMMAND="yarn run test:watch"
;;
*)
break
esac
shift
done
# * If not mode has been given in the CLI, print help and exit
if [ -z "$mode" ]; then
help $0
fi
# * Load variables from .env file so it is used anywhere in the docker-compose files
if [ -f .env.$mode ]
then
export $(cat .env.$mode | sed 's/#.*//g' | xargs)
fi
await_console() {
if [ "$mode" != "test" ]; then
# Wait for Hasura Graphql Engine" before starting the console
echo "Waiting for Hasura Graphql Engine to be ready..."
until $(curl -X GET --output /dev/null --silent --head --fail http://localhost:8080/healthz); do
sleep 1
done
# Set the Hasura config.yaml file
printf 'endpoint: http://localhost:8080\nadmin_secret: %s\n' $HASURA_GRAPHQL_ADMIN_SECRET > config.yaml
hasura console
fi
}
echo "Running mode '$mode'..."
trap clean_exit INT
clean_exit() {
echo "Cleaning up..."
if [ "$mode" != "test" ]; then # Kill Hasura Console
ps -ef | grep 'hasura console' | grep -v grep | awk '{print $2}' | xargs kill -9
fi
# Stop all docker services
docker-compose -p "hbp_${mode}" down $remove --remove-orphans
exit
}
# * Build the docker images first, if the build option has been passed on
if [ -n "$build" ]; then
docker-compose -p "hbp_$mode" -f docker-compose.yaml -f docker-compose.$mode.yaml build
fi
# * Start on background in waiting for Hasura to be ready so the console can be launched
await_console &
# * Start docker services
docker-compose -p "hbp_$mode" -f docker-compose.yaml -f docker-compose.$mode.yaml run --service-ports --use-aliases hasura-backend-plus $COMMAND
clean_exit

View File

@@ -1,101 +0,0 @@
#!/bin/bash
source bash-utils.sh
function help() {
echo "Usage:"
printf "\t${0} <command> [flags]\n"
echo "Flags:"
printf "\t-h, --help\t\tShow help\n"
printf "\t-b, --build\t\tForce build the docker file\n"
echo "Commands:"
printf "\tdev\t Development without runnig any test\n"
printf "\twatch\t Development with Jest watching\n"
printf "\ttest\t Run Jest tests\n"
}
export NODE_ENV="test"
while :; do
case $1 in
-h|-\?|--help)
help $0
exit
;;
-b|--build)
build="--build"
;;
--)
shift
break
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
test)
mode="test"
export COMMAND_LINE="yarn run test"
;;
dev)
mode="dev"
export NODE_ENV="development"
;;
watch)
mode="watch"
# Use another internal port (4000) to run the dev server so Puppeteer Jest can use the default port (3000) in the local docker context
export PORT=4000
;;
*)
break
esac
shift
done
if [ -n "$mode" ]; then
echo "Running mode '$mode'..."
else
help $0
exit
fi
if [ ! -f .env.dockerdev ]; then
echo "File .env.dockerdev not found! Creating an empty file."
echo "Please set the secrets as per described in .env.dockerdev.example. Otherwise some tests could be skipped."
touch .env.dockerdev
fi
# Fetch variables from .env.test so it is used in the docker-compose files
export-dotenv ".env.$NODE_ENV" HASURA_GRAPHQL_ADMIN_SECRET
export-dotenv ".env.$NODE_ENV" JWT_KEY
# Load the variables required for the Minio service
export-dotenv ".env.$NODE_ENV" S3_SECRET_ACCESS_KEY
export-dotenv ".env.$NODE_ENV" S3_ACCESS_KEY_ID
# Start docker services
docker-compose -p "hbp_${mode}" -f docker-compose.yaml -f docker-compose.dev.yaml up -d $build
trap exit_script INT
function exit_script() {
echo "Cleaning up..."
if [ "$mode" != "test" ]; then
# Kill Hasura Console
ps -ef | grep 'hasura console' | grep -v grep | awk '{print $2}' | xargs kill -9
fi
# Stop and remove all docker images, volumes and networks
docker-compose -p "hbp_${mode}" down -v --remove-orphans
exit
}
if [ "$mode" != "test" ]; then
# NOTE: The Hasura console should accessed from the CLI so the migration files can be automatically generated
# Waith for Hasura Graphql Engine" before starting the console
wait-for http://localhost:8080/healthz "Hasura Graphql Engine"
# Set the Hasura config.yaml file
printf 'endpoint: http://localhost:8080\nHASURA_GRAPHQL_ADMIN_SECRET: %s\n' $HASURA_GRAPHQL_ADMIN_SECRET > config.yaml
hasura console &
fi
if [ "$mode" == "watch" ]; then
# Run Jest on watch mode
docker exec -it -e PORT=3000 -e NODE_ENV=test "hbp_${mode}_hasura-backend-plus_1" yarn test:watch
else
docker container logs -f "hbp_${mode}_hasura-backend-plus_1"
fi
exit_script

View File

@@ -4,8 +4,9 @@
"compilerOptions": {
"lib": ["dom", "es2020"],
"paths": {
"*": ["types/*"],
"@shared/*": ["src/shared/*"]
"*": ["src/types/*"],
"@shared/*": ["src/shared/*"],
"@test/*": ["src/test/*"]
},
"module": "commonjs",
"outDir": "dist",
@@ -15,6 +16,7 @@
"noUnusedLocals": true,
"esModuleInterop": true,
"moduleResolution": "node",
"strictNullChecks": true
"strictNullChecks": true,
"resolveJsonModule": true
}
}

View File

@@ -2323,6 +2323,12 @@ aws4@^1.8.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
axios@^0.19.0:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
dependencies:
follow-redirects "1.5.10"
babel-jest@^25.3.0:
version "25.3.0"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.3.0.tgz#999d0c19e8427f66b796bf9ea233eedf087b957c"
@@ -3795,18 +3801,18 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@=3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
dependencies:
ms "^2.1.1"
debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decamelize@^1.0.0, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -4889,6 +4895,12 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
dependencies:
debug "=3.1.0"
follow-redirects@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb"
@@ -5335,6 +5347,13 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hasura-cli@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/hasura-cli/-/hasura-cli-1.1.1.tgz#d1c89e4232641f3b0f51019072d4d1042568dea4"
dependencies:
axios "^0.19.0"
chalk "^2.4.2"
he@1.2.0, he@1.2.x, he@^1.1.0, he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"