Merge branch 'master' into setup-tests-for-resolvers

This commit is contained in:
Moriz Wahl 2021-10-20 20:24:48 +02:00 committed by Ulf Gebhardt
commit c20e17d11a
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
131 changed files with 1738 additions and 996 deletions

View File

@ -72,7 +72,7 @@ jobs:
##########################################################################
- name: Backend | Build `production` image
run: |
docker build --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" backend/
docker build -f ./backend/Dockerfile --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" .
docker save "gradido/backend" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2

View File

@ -47,7 +47,7 @@ jobs:
##########################################################################
- name: Backend | Build `test` image
run: |
docker build --target test -t "gradido/backend:test" backend/
docker build -f ./backend/Dockerfile --target test -t "gradido/backend:test" backend/
docker save "gradido/backend:test" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
@ -147,7 +147,7 @@ jobs:
##########################################################################
- name: mariadb | Build `test` image
run: |
docker build --target mariadb_server_test -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./
docker build --target mariadb_server -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./
docker save "gradido/mariadb:test" > /tmp/mariadb.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
@ -344,7 +344,7 @@ jobs:
report_name: Coverage Frontend
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 82
min_coverage: 83
token: ${{ github.token }}
##############################################################################
@ -416,10 +416,16 @@ jobs:
--health-timeout=3s
--health-retries=4
steps:
- name: Debug service
run: echo "$(docker ps)"
- name: Debug container choosing script
run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
# - name: Debug service
# run: echo "$(docker ps)"
#- name: Debug container choosing script
# run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
- name: get mariadb container id
run: echo "::set-output name=id::$(docker container ls | grep mariadb | awk '{ print $1 }')"
id: mariadb_container
- name: get automatic created network
run: echo "::set-output name=id::$(docker network ls | grep github_network | awk '{ print $1 }')"
id: network
##########################################################################
# CHECKOUT CODE ##########################################################
##########################################################################
@ -427,6 +433,11 @@ jobs:
uses: actions/checkout@v2
with:
submodules: true
# Database migration
- name: Start database migration
run: |
docker build --target production_up -t "gradido/database:production_up" database/
docker run --network ${{ steps.network.outputs.id }} --name=database --env NODE_ENV=production --env DB_HOST=mariadb --env DB_DATABASE=gradido_community_test -d gradido/database:production_up
##########################################################################
# Build Login-Server Test Docker image ###################################
##########################################################################

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
/node_modules/*
.vscode
messages.pot
.skeema
nbproject
.metadata
/.env

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

View File

@ -42,6 +42,8 @@ EXPOSE ${PORT}
RUN mkdir -p ${DOCKER_WORKDIR}
WORKDIR ${DOCKER_WORKDIR}
RUN mkdir -p /database
##################################################################################
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
##################################################################################
@ -51,22 +53,32 @@ FROM base as development
# local filesystem which will need a rebuild anyway
# Run command
# (for development we need to execute npm install since the
# (for development we need to execute yarn install since the
# node_modules are on another volume and need updating)
CMD /bin/sh -c "yarn install && yarn run dev"
CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
##################################################################################
# BUILD (Does contain all files and is therefore bloated) ########################
##################################################################################
FROM base as build
# Copy everything
COPY . .
# npm install
# Copy everything from backend
COPY ./backend/ ./
# Copy everything from database
COPY ./database/ ../database/
# yarn install backend
RUN yarn install --production=false --frozen-lockfile --non-interactive
# npm build
# yarn install database
RUN cd ../database && yarn install --production=false --frozen-lockfile --non-interactive
# yarn build
RUN yarn run build
# yarn build database
RUN cd ../database && yarn run build
##################################################################################
# TEST ###########################################################################
##################################################################################
@ -82,8 +94,10 @@ FROM base as production
# Copy "binary"-files from build image
COPY --from=build ${DOCKER_WORKDIR}/build ./build
COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build
# We also copy the node_modules express and serve-static for the run script
# COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules
# Copy static files
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
# Copy package.json for script definitions (lock file should not be needed)

View File

@ -4,4 +4,7 @@ module.exports = {
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
moduleNameMapper: {
'@entity/(.*)': '<rootDir>/../database/entity/$1',
},
}

View File

@ -28,11 +28,12 @@
"jest": "^27.2.4",
"jsonwebtoken": "^8.5.1",
"libsodium-wrappers": "^0.7.9",
"module-alias": "^2.2.2",
"mysql2": "^2.3.0",
"reflect-metadata": "^0.1.13",
"ts-jest": "^27.0.5",
"type-graphql": "^1.1.1",
"typeorm": "^0.2.37"
"typeorm": "^0.2.38"
},
"devDependencies": {
"@types/express": "^4.17.12",
@ -51,5 +52,8 @@
"prettier": "^2.3.1",
"ts-node": "^10.0.0",
"typescript": "^4.3.4"
},
"_moduleAliases": {
"@entity" : "../database/build/entity"
}
}

View File

@ -19,12 +19,12 @@ import { UserRepository } from '../../typeorm/repository/User'
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
import { TransactionRepository } from '../../typeorm/repository/Transaction'
import { User as dbUser } from '../../typeorm/entity/User'
import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction'
import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction'
import { User as dbUser } from '@entity/User'
import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction'
import { Transaction as dbTransaction } from '@entity/Transaction'
import { apiPost } from '../../apis/HttpRequest'
import { roundFloorFrom4 } from '../../util/round'
import { roundFloorFrom4, roundCeilFrom4 } from '../../util/round'
import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
import { TransactionTypeId } from '../enum/TransactionTypeId'
import { TransactionType } from '../enum/TransactionType'
@ -150,7 +150,7 @@ async function calculateAndAddDecayTransactions(
const decayTransaction = new Transaction()
decayTransaction.type = 'decay'
decayTransaction.balance = roundFloorFrom4(balance)
decayTransaction.balance = roundCeilFrom4(balance)
decayTransaction.decayDuration = decay.decayDuration
decayTransaction.decayStart = decay.decayStart
decayTransaction.decayEnd = decay.decayEnd

View File

@ -9,7 +9,7 @@ import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
import { UpdateUserInfosResponse } from '../model/UpdateUserInfosResponse'
import { User } from '../model/User'
import { User as DbUser } from '../../typeorm/entity/User'
import { User as DbUser } from '@entity/User'
import encode from '../../jwt/encode'
import ChangePasswordArgs from '../arg/ChangePasswordArgs'
import CheckUsernameArgs from '../arg/CheckUsernameArgs'

View File

@ -1,6 +1,6 @@
import { createConnection, Connection } from 'typeorm'
import CONFIG from '../config'
import path from 'path'
import { entities } from '@entity/index'
const connection = async (): Promise<Connection | null> => {
let con = null
@ -13,7 +13,7 @@ const connection = async (): Promise<Connection | null> => {
username: CONFIG.DB_USER,
password: CONFIG.DB_PASSWORD,
database: CONFIG.DB_DATABASE,
entities: [path.join(__dirname, 'entity', '*.{ts,js}')],
entities,
synchronize: false,
})
} catch (error) {

View File

@ -1,10 +1,13 @@
import { Migration } from './entity/Migration'
import { getRepository } from 'typeorm'
import { Migration } from '@entity/Migration'
const getDBVersion = async (): Promise<string | null> => {
try {
const dbVersion = await Migration.findOne({ order: { version: 'DESC' } })
const dbVersion = await getRepository(Migration).findOne({ order: { version: 'DESC' } })
return dbVersion ? dbVersion.fileName : null
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
return null
}
}

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { Balance } from '../entity/Balance'
import { Balance } from '@entity/Balance'
@EntityRepository(Balance)
export class BalanceRepository extends Repository<Balance> {

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { Transaction } from '../entity/Transaction'
import { Transaction } from '@entity/Transaction'
@EntityRepository(Transaction)
export class TransactionRepository extends Repository<Transaction> {

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { User } from '../entity/User'
import { User } from '@entity/User'
@EntityRepository(User)
export class UserRepository extends Repository<User> {

View File

@ -1,5 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { UserSetting } from '../entity/UserSetting'
import { UserSetting } from '@entity/UserSetting'
import { Setting } from '../../graphql/enum/Setting'
import { isStringBoolean } from '../../util/validate'

View File

@ -1,6 +1,6 @@
import { EntityRepository, Repository } from 'typeorm'
import { Order } from '../../graphql/enum/Order'
import { UserTransaction } from '../entity/UserTransaction'
import { UserTransaction } from '@entity/UserTransaction'
@EntityRepository(UserTransaction)
export class UserTransactionRepository extends Repository<UserTransaction> {

View File

@ -46,7 +46,9 @@
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"@entity/*": ["../database/entity/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
@ -68,5 +70,12 @@
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
},
"references": [
{
"path": "../database/tsconfig.json",
// add 'prepend' if you want to include the referenced project in your output file
// "prepend": true,
}
]
}

View File

@ -4123,6 +4123,11 @@ mkdirp@^1.0.4:
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
module-alias@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
@ -5374,10 +5379,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typeorm@^0.2.37:
version "0.2.37"
resolved "https://registry.npmjs.org/typeorm/-/typeorm-0.2.37.tgz"
integrity sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==
typeorm@^0.2.38:
version "0.2.38"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.38.tgz#2af08079919f6ab04cd17017f9faa2c8d5cd566f"
integrity sha512-M6Y3KQcAREQcphOVJciywf4mv6+A0I/SeR+lWNjKsjnQ+a3XcMwGYMGL0Jonsx3H0Cqlf/3yYqVki1jIXSK/xg==
dependencies:
"@sqltools/formatter" "^1.2.2"
app-root-path "^3.0.0"

View File

@ -15,7 +15,8 @@ class ServerUsersController extends AppController
public function initialize()
{
parent::initialize();
$this->Auth->allow(['add', 'edit']);
// uncomment in devmode to add new community server admin user, but don't!!! commit it
//$this->Auth->allow(['add', 'edit']);
$this->Auth->deny('index');
}

View File

@ -17,7 +17,7 @@ phpServer.host = nginx
loginServer.path = http://localhost/account
loginServer.default_locale = de
loginServer.db.host = mariadb
loginServer.db.name = gradido_login
loginServer.db.name = gradido_community
loginServer.db.user = root
loginServer.db.password =
loginServer.db.port = 3306

View File

@ -17,7 +17,7 @@ phpServer.host = nginx
loginServer.path = http://localhost/account
loginServer.default_locale = de
loginServer.db.host = mariadb
loginServer.db.name = gradido_login_test
loginServer.db.name = gradido_community_test
loginServer.db.user = root
loginServer.db.password =
loginServer.db.port = 3306

View File

@ -1,6 +0,0 @@
INSERT INTO `groups` (`id`, `alias`, `name`, `url`, `host`, `home`, `description`) VALUES
(1, 'docker', 'docker gradido group', 'localhost', 'nginx', '/', 'gradido test group for docker and stage2 with blockchain db');

View File

@ -73,7 +73,7 @@ RUN yarn run build
FROM build as test_up
# Run command
CMD /bin/sh -c "yarn run dev_up"
CMD /bin/sh -c "yarn install && yarn run dev_up"
##################################################################################
# TEST RESET #####################################################################
@ -81,7 +81,7 @@ CMD /bin/sh -c "yarn run dev_up"
FROM build as test_reset
# Run command
CMD /bin/sh -c "yarn run dev_reset"
CMD /bin/sh -c "yarn install && yarn run dev_reset"
##################################################################################
# TEST DOWN ######################################################################
@ -89,7 +89,7 @@ CMD /bin/sh -c "yarn run dev_reset"
FROM build as test_down
# Run command
CMD /bin/sh -c "yarn run dev_down"
CMD /bin/sh -c "yarn install && yarn run dev_down"
##################################################################################
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #

View File

@ -0,0 +1,26 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
// Moriz: I do not like the idea of having two user tables
@Entity('state_users')
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer
@Column()
email: string
@Column({ name: 'first_name' })
firstName: string
@Column({ name: 'last_name' })
lastName: string
@Column()
username: string
@Column()
disabled: boolean
}

View File

@ -7,9 +7,6 @@ export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
// @ManyToOne(type => Group, group => group.users)
// group: Group;
@Column({ type: 'binary', length: 32, name: 'public_key' })
pubkey: Buffer

View File

@ -0,0 +1 @@
export { Balance } from './0001-init_db/Balance'

View File

@ -0,0 +1 @@
export { Migration } from './0001-init_db/Migration'

View File

@ -0,0 +1 @@
export { Transaction } from './0001-init_db/Transaction'

View File

@ -0,0 +1 @@
export { TransactionCreation } from './0001-init_db/TransactionCreation'

View File

@ -0,0 +1 @@
export { TransactionSendCoin } from './0001-init_db/TransactionSendCoin'

1
database/entity/User.ts Normal file
View File

@ -0,0 +1 @@
export { User } from './0002-add_settings/User'

View File

@ -0,0 +1 @@
export { UserSetting } from './0002-add_settings/UserSetting'

View File

@ -0,0 +1 @@
export { UserTransaction } from './0001-init_db/UserTransaction'

19
database/entity/index.ts Normal file
View File

@ -0,0 +1,19 @@
import { Balance } from './Balance'
import { Migration } from './Migration'
import { Transaction } from './Transaction'
import { TransactionCreation } from './TransactionCreation'
import { TransactionSendCoin } from './TransactionSendCoin'
import { User } from './User'
import { UserSetting } from './UserSetting'
import { UserTransaction } from './UserTransaction'
export const entities = [
Balance,
Migration,
Transaction,
TransactionCreation,
TransactionSendCoin,
User,
UserSetting,
UserTransaction,
]

View File

@ -11,7 +11,7 @@
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE IF NOT EXISTS \`user_setting\` (
CREATE TABLE \`user_setting\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`userId\` int(11) NOT NULL,
\`key\` varchar(255) NOT NULL,
@ -22,5 +22,5 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// write downgrade logic as parameter of queryFn
await queryFn(`DROP TABLE IF EXISTS \`user_setting\`;`)
await queryFn(`DROP TABLE \`user_setting\`;`)
}

View File

@ -0,0 +1,153 @@
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
* the database can be setup already but also may not be.
* Therefore you will find all `CREATE TABLE` statements with
* a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
* downgrade function all `DROP TABLE` with a `IF EXISTS`.
* This ensures compatibility for existing or non-existing
* databases.
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE \`login_app_access_tokens\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int NOT NULL,
\`access_code\` bigint unsigned NOT NULL,
\`created\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`access_code\` (\`access_code\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_elopage_buys\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`elopage_user_id\` int DEFAULT NULL,
\`affiliate_program_id\` int NOT NULL,
\`publisher_id\` int NOT NULL,
\`order_id\` int NOT NULL,
\`product_id\` int NOT NULL,
\`product_price\` int NOT NULL,
\`payer_email\` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
\`publisher_email\` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
\`payed\` tinyint NOT NULL,
\`success_date\` datetime NOT NULL,
\`event\` varchar(255) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_email_opt_in_types\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(255) NOT NULL,
\`description\` varchar(255) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_email_opt_in\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int NOT NULL,
\`verification_code\` bigint unsigned NOT NULL,
\`email_opt_in_type_id\` int NOT NULL,
\`created\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`resend_count\` int DEFAULT '0',
\`updated\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`verification_code\` (\`verification_code\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_groups\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`alias\` varchar(190) NOT NULL,
\`name\` varchar(255) NOT NULL,
\`url\` varchar(255) NOT NULL,
\`host\` varchar(255) DEFAULT "/",
\`home\` varchar(255) DEFAULT "/",
\`description\` text,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`alias\` (\`alias\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_pending_tasks\` (
\`id\` int UNSIGNED NOT NULL AUTO_INCREMENT,
\`user_id\` int UNSIGNED DEFAULT 0,
\`request\` varbinary(2048) NOT NULL,
\`created\` datetime NOT NULL,
\`finished\` datetime DEFAULT '2000-01-01 000000',
\`result_json\` text DEFAULT NULL,
\`param_json\` text DEFAULT NULL,
\`task_type_id\` int UNSIGNED NOT NULL,
\`child_pending_task_id\` int UNSIGNED DEFAULT 0,
\`parent_pending_task_id\` int UNSIGNED DEFAULT 0,
PRIMARY KEY (\`id\`)
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_roles\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(255) NOT NULL,
\`description\` varchar(255) NOT NULL,
\`flags\` bigint NOT NULL DEFAULT '0',
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_user_backups\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int NOT NULL,
\`passphrase\` text NOT NULL,
\`mnemonic_type\` int DEFAULT '-1',
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_user_roles\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int NOT NULL,
\`role_id\` int NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`login_users\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`email\` varchar(191) NOT NULL,
\`first_name\` varchar(150) NOT NULL,
\`last_name\` varchar(255) DEFAULT '',
\`username\` varchar(255) DEFAULT '',
\`description\` text DEFAULT '',
\`password\` bigint unsigned DEFAULT '0',
\`pubkey\` binary(32) DEFAULT NULL,
\`privkey\` binary(80) DEFAULT NULL,
\`email_hash\` binary(32) DEFAULT NULL,
\`created\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`email_checked\` tinyint NOT NULL DEFAULT '0',
\`passphrase_shown\` tinyint NOT NULL DEFAULT '0',
\`language\` varchar(4) NOT NULL DEFAULT 'de',
\`disabled\` tinyint DEFAULT '0',
\`group_id\` int unsigned DEFAULT 0,
\`publisher_id\` int DEFAULT 0,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`email\` (\`email\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// write downgrade logic as parameter of queryFn
await queryFn(`DROP TABLE \`login_app_access_tokens\`;`)
await queryFn(`DROP TABLE \`login_elopage_buys\`;`)
await queryFn(`DROP TABLE \`login_email_opt_in_types\`;`)
await queryFn(`DROP TABLE \`login_email_opt_in\`;`)
await queryFn(`DROP TABLE \`login_groups\`;`)
await queryFn(`DROP TABLE \`login_pending_tasks\`;`)
await queryFn(`DROP TABLE \`login_roles\`;`)
await queryFn(`DROP TABLE \`login_user_backups\`;`)
await queryFn(`DROP TABLE \`login_user_roles\`;`)
await queryFn(`DROP TABLE \`login_users\`;`)
}

View File

@ -0,0 +1,74 @@
/* FIRST MIGRATION
*
* This migration is special since it takes into account that
* the database can be setup already but also may not be.
* Therefore you will find all `CREATE TABLE` statements with
* a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
* downgrade function all `DROP TABLE` with a `IF EXISTS`.
* This ensures compatibility for existing or non-existing
* databases.
*/
const LOGIN_SERVER_DB = 'gradido_login'
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
const loginDatabaseExists = await queryFn(`
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${LOGIN_SERVER_DB}'
`)
if (loginDatabaseExists.length === 0) {
// eslint-disable-next-line no-console
console.log(`Skipping Login Server Database migration - Database ${LOGIN_SERVER_DB} not found`)
return
}
await queryFn(`
INSERT INTO \`login_app_access_tokens\` SELECT * FROM ${LOGIN_SERVER_DB}.\`app_access_tokens\`;
`)
await queryFn(`
INSERT INTO \`login_elopage_buys\` SELECT * FROM ${LOGIN_SERVER_DB}.\`elopage_buys\`;
`)
await queryFn(`
INSERT INTO \`login_email_opt_in_types\` SELECT * FROM ${LOGIN_SERVER_DB}.\`email_opt_in_types\`;
`)
await queryFn(`
INSERT INTO \`login_email_opt_in\` SELECT * FROM ${LOGIN_SERVER_DB}.\`email_opt_in\`;
`)
await queryFn(`
INSERT INTO \`login_groups\` SELECT * FROM ${LOGIN_SERVER_DB}.\`groups\`;
`)
await queryFn(`
INSERT INTO \`login_pending_tasks\` SELECT * FROM ${LOGIN_SERVER_DB}.\`pending_tasks\`;
`)
await queryFn(`
INSERT INTO \`login_roles\` SELECT * FROM ${LOGIN_SERVER_DB}.\`roles\`;
`)
await queryFn(`
INSERT INTO \`login_user_backups\` SELECT * FROM ${LOGIN_SERVER_DB}.\`user_backups\`;
`)
await queryFn(`
INSERT INTO \`login_user_roles\` SELECT * FROM ${LOGIN_SERVER_DB}.\`user_roles\`;
`)
await queryFn(`
INSERT INTO \`login_users\` SELECT * FROM ${LOGIN_SERVER_DB}.\`users\`;
`)
// TODO clarify if we need this on non docker environment?
await queryFn(`
INSERT IGNORE INTO \`login_groups\` (\`id\`, \`alias\`, \`name\`, \`url\`, \`host\`, \`home\`, \`description\`) VALUES
(1, 'docker', 'docker gradido group', 'localhost', 'nginx', '/', 'gradido test group for docker and stage2 with blockchain db');
`)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// write downgrade logic as parameter of queryFn
await queryFn(`DELETE FROM \`login_app_access_tokens\`;`)
await queryFn(`DELETE FROM \`login_elopage_buys\`;`)
await queryFn(`DELETE FROM \`login_email_opt_in_types\`;`)
await queryFn(`DELETE FROM \`login_email_opt_in\`;`)
await queryFn(`DELETE FROM \`login_groups\`;`)
await queryFn(`DELETE FROM \`login_pending_tasks\`;`)
await queryFn(`DELETE FROM \`login_roles\`;`)
await queryFn(`DELETE FROM \`login_user_backups\`;`)
await queryFn(`DELETE FROM \`login_user_roles\`;`)
await queryFn(`DELETE FROM \`login_users\`;`)
}

View File

@ -19,7 +19,7 @@
"lint": "eslint . --ext .js,.ts"
},
"devDependencies": {
"@types/node": "^16.7.1",
"@types/node": "^16.10.3",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"eslint": "^7.32.0",
@ -37,6 +37,8 @@
"dependencies": {
"dotenv": "^10.0.0",
"mysql2": "^2.3.0",
"ts-mysql-migrate": "^1.0.2"
"reflect-metadata": "^0.1.13",
"ts-mysql-migrate": "^1.0.2",
"typeorm": "^0.2.38"
}
}

View File

@ -1,13 +1,15 @@
import 'reflect-metadata'
import { createPool, PoolConfig } from 'mysql'
import { Migration } from 'ts-mysql-migrate'
import CONFIG from './config'
import prepare from './prepare'
import connection from './typeorm/connection'
const run = async (command: string) => {
// Database actions not supported by our migration library
await prepare()
// Database connection
// Database connection for Migrations
const poolConfig: PoolConfig = {
host: CONFIG.DB_HOST,
port: CONFIG.DB_PORT,
@ -26,6 +28,12 @@ const run = async (command: string) => {
dir: CONFIG.MIGRATIONS_DIRECTORY,
})
// Database connection for TypeORM
const con = await connection()
if (!con || !con.isConnected) {
throw new Error(`Couldn't open connection to database`)
}
await migration.initialize()
// Execute command
@ -42,6 +50,10 @@ const run = async (command: string) => {
default:
throw new Error(`Unsupported command ${command}`)
}
// Terminate connections gracefully
await con.close()
pool.end()
}
run(process.argv[2])

View File

@ -0,0 +1,27 @@
import { createConnection, Connection } from 'typeorm'
import CONFIG from '../config'
import { entities } from '../../entity/index'
const connection = async (): Promise<Connection | null> => {
let con = null
try {
con = await createConnection({
name: 'default',
type: 'mysql',
host: CONFIG.DB_HOST,
port: CONFIG.DB_PORT,
username: CONFIG.DB_USER,
password: CONFIG.DB_PASSWORD,
database: CONFIG.DB_DATABASE,
entities,
synchronize: false,
})
} catch (error) {
// eslint-disable-next-line no-console
console.log(error)
}
return con
}
export default connection

View File

@ -4,19 +4,19 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */
"outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
"composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
@ -47,7 +47,7 @@
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
@ -68,5 +68,6 @@
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
},
"references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */
}

View File

@ -90,6 +90,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sqltools/formatter@^1.2.2":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20"
integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@ -129,11 +134,21 @@
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^16.7.1":
"@types/node@*":
version "16.7.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0"
integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==
"@types/node@^16.10.3":
version "16.10.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5"
integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==
"@types/zen-observable@0.8.3":
version "0.8.3"
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3"
integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==
"@typescript-eslint/eslint-plugin@^4.29.2":
version "4.29.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.2.tgz#f54dc0a32b8f61c6024ab8755da05363b733838d"
@ -260,6 +275,11 @@ ansi-colors@^4.1.1:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
@ -270,6 +290,11 @@ ansi-regex@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@ -284,6 +309,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
@ -292,6 +322,11 @@ anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
app-root-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@ -304,6 +339,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
array-includes@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
@ -339,6 +379,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bignumber.js@9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
@ -378,6 +423,14 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
@ -409,6 +462,17 @@ camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
chalk@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
dependencies:
ansi-styles "^2.2.1"
escape-string-regexp "^1.0.2"
has-ansi "^2.0.0"
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@ -426,7 +490,7 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0:
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@ -459,6 +523,27 @@ cli-boxes@^2.2.0:
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
cli-highlight@^2.1.11:
version "2.1.11"
resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf"
integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==
dependencies:
chalk "^4.0.0"
highlight.js "^10.7.1"
mz "^2.4.0"
parse5 "^5.1.1"
parse5-htmlparser2-tree-adapter "^6.0.0"
yargs "^16.0.0"
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
@ -624,6 +709,11 @@ dotenv@^10.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
dotenv@^8.2.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@ -692,12 +782,17 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-goat@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
escape-string-regexp@^1.0.5:
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
@ -945,6 +1040,11 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
figlet@^1.1.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634"
integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@ -1006,6 +1106,11 @@ generate-function@^2.3.1:
dependencies:
is-property "^1.0.2"
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
@ -1048,6 +1153,18 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.6:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
@ -1096,6 +1213,13 @@ graceful-fs@^4.1.2:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
dependencies:
ansi-regex "^2.0.0"
has-bigints@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@ -1135,6 +1259,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
highlight.js@^10.7.1:
version "10.7.3"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@ -1152,6 +1281,11 @@ iconv-lite@^0.6.2:
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
@ -1193,7 +1327,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@~2.0.3:
inherits@2, inherits@^2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1392,6 +1526,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
@ -1551,6 +1692,11 @@ minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -1590,6 +1736,15 @@ mysql@^2.18.1:
safe-buffer "5.1.2"
sqlstring "2.3.1"
mz@^2.4.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
dependencies:
any-promise "^1.0.0"
object-assign "^4.0.1"
thenify-all "^1.0.0"
named-placeholders@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8"
@ -1645,6 +1800,11 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-inspect@^1.11.0, object-inspect@^1.9.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
@ -1734,6 +1894,11 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
parent-require@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977"
integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@ -1742,6 +1907,23 @@ parse-json@^4.0.0:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
parse5-htmlparser2-tree-adapter@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
dependencies:
parse5 "^6.0.1"
parse5@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@ -1912,6 +2094,11 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
reflect-metadata@^0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
regexpp@^3.0.0, regexpp@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@ -1931,6 +2118,11 @@ registry-url@^5.0.0:
dependencies:
rc "^1.2.8"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@ -1980,11 +2172,21 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@>=0.6.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver-diff@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
@ -2014,6 +2216,14 @@ seq-queue@^0.0.5:
resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=
sha.js@^2.4.11:
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@ -2136,6 +2346,13 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@ -2165,6 +2382,11 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@ -2201,6 +2423,20 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
dependencies:
thenify ">= 3.1.0 < 4"
"thenify@>= 3.1.0 < 4":
version "3.3.1"
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
dependencies:
any-promise "^1.0.0"
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
@ -2260,6 +2496,11 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
@ -2291,6 +2532,29 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typeorm@^0.2.38:
version "0.2.38"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.38.tgz#2af08079919f6ab04cd17017f9faa2c8d5cd566f"
integrity sha512-M6Y3KQcAREQcphOVJciywf4mv6+A0I/SeR+lWNjKsjnQ+a3XcMwGYMGL0Jonsx3H0Cqlf/3yYqVki1jIXSK/xg==
dependencies:
"@sqltools/formatter" "^1.2.2"
app-root-path "^3.0.0"
buffer "^6.0.3"
chalk "^4.1.0"
cli-highlight "^2.1.11"
debug "^4.3.1"
dotenv "^8.2.0"
glob "^7.1.6"
js-yaml "^4.0.0"
mkdirp "^1.0.4"
reflect-metadata "^0.1.13"
sha.js "^2.4.11"
tslib "^2.1.0"
xml2js "^0.4.23"
yargonaut "^1.1.4"
yargs "^17.0.1"
zen-observable-ts "^1.0.0"
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
@ -2401,6 +2665,15 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@ -2421,6 +2694,24 @@ xdg-basedir@^4.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
xml2js@^0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
@ -2431,7 +2722,60 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yargonaut@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c"
integrity sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==
dependencies:
chalk "^1.1.1"
figlet "^1.1.1"
parent-require "^1.0.0"
yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yargs@^16.0.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs@^17.0.1:
version "17.2.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea"
integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
y18n "^5.0.5"
yargs-parser "^20.2.2"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
zen-observable-ts@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83"
integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==
dependencies:
"@types/zen-observable" "0.8.3"
zen-observable "0.8.15"
zen-observable@0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==

View File

@ -36,8 +36,6 @@ cd $PROJECT_PATH
# git checkout -f master
git pull
cd deployment/bare_metal
echo 'update schemas' >> $UPDATE_HTML
./update_db_schemas.sh
echo 'starting with rebuilding login-server<br>' >> $UPDATE_HTML
./build_and_start_login_server.sh
echo 'starting with rebuilding frontend<br>' >> $UPDATE_HTML

View File

@ -1,7 +0,0 @@
#!/bin/bash
# For that to work skeema needed to be installed on system
# in login_server/skeema skeema configuration files need to be there also in the subfolders
# Update DB Schemas (only the schemas, not the data)
cd ../../login_server/skeema
skeema push --allow-unsafe

View File

@ -36,8 +36,11 @@ services:
# This makes sure the docker container has its own node modules.
# Therefore it is possible to have a different node version on the host machine
- backend_node_modules:/app/node_modules
- backend_database_node_modules:/database/node_modules
- backend_database_build:/database/build
# bind the local folder to the docker to allow live reload
- ./backend:/app
- ./database:/database
########################################################
# DATABASE ##############################################
@ -134,24 +137,11 @@ services:
volumes:
- /sessions
#########################################################
## skeema for updating dbs if changes happend ###########
#########################################################
skeema:
build:
context: .
dockerfile: ./skeema/Dockerfile
target: skeema_dev_run
depends_on:
- mariadb
networks:
- internal-net
volumes:
- ./login_server/skeema/gradido_login:/skeema/gradido_login
volumes:
frontend_node_modules:
backend_node_modules:
backend_database_node_modules:
backend_database_build:
database_node_modules:
database_build:
login_build_ubuntu_3.1:

View File

@ -23,7 +23,7 @@ services:
build:
context: .
dockerfile: ./mariadb/Dockerfile
target: mariadb_server_test
target: mariadb_server
environment:
- MARIADB_ALLOW_EMPTY_PASSWORD=1
- MARIADB_USER=root
@ -90,19 +90,6 @@ services:
volumes:
- /sessions
#########################################################
## skeema for updating dbs if changes happend ###########
#########################################################
skeema:
build:
context: .
dockerfile: ./skeema/Dockerfile
target: skeema_run
depends_on:
- mariadb
networks:
- internal-net
networks:
external-net:
internal-net:

View File

@ -35,9 +35,8 @@ services:
#########################################################
mariadb:
build:
context: .
dockerfile: ./mariadb/Dockerfile
target: mariadb_server_test
context: ./mariadb
target: mariadb_server
environment:
- MARIADB_ALLOW_EMPTY_PASSWORD=1
- MARIADB_USER=root
@ -54,7 +53,10 @@ services:
backend:
image: gradido/backend:latest
build:
context: ./backend
# since we have to include the entities from ./database we cannot define the context as ./backend
# this might blow build image size to the moon ?!
context: ./
dockerfile: ./backend/Dockerfile
target: production
networks:
- internal-net
@ -156,41 +158,6 @@ services:
volumes:
- ./community_server/config/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini
#########################################################
## skeema for updating dbs if changes happend ###########
#########################################################
skeema:
build:
context: .
dockerfile: ./skeema/Dockerfile
target: skeema_run
depends_on:
- mariadb
networks:
- internal-net
#########################################################
## GRADIDO NODE v1 ######################################
#########################################################
# gradido-node:
# build:
# context: .
# dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile
# volumes:
# - ${GN_INSTANCE_FOLDER}:/opt/instance
# container_name: ${GN_CONTAINER_NAME}
#########################################################
## GRADIDO NODE test ###################################
#########################################################
# gradido-node-test:
# build:
# context: .
# dockerfile: ./gn/docker/deprecated-hedera-node/Dockerfile
# container_name: gn-test
# working_dir: /opt/gn/build
# command: ["./unit_tests"]
networks:
external-net:
internal-net:

View File

@ -15,7 +15,7 @@ This document describes the technical overview for the Gradido infrastructur. Be
![CommunityServerAPI](../image/CommunityServerAPI.png)
### Database Skeema
### Database Skeema (outdated)
![CommunityDBSkeema](../image/CommunityDBSkeema.png)

BIN
docu/Gradido-Admin.epgz Normal file

Binary file not shown.

View File

@ -0,0 +1,27 @@
### User creation
A user needs to be created on the login_server we do this when we create a User in the client https://$community_domain/vue/register.
### Admin user
To set a User admin we need the following SQL query on the gradido_login database:
```
INSERT INTO user_roles (id, user_id, role_id) VALUES (NULL, '1', '1');
```
user_id has to be found in users
Now when we login in on https://$community_domain/account/ we can create coins but we will be restricted cause we can't sign the creations.
### Signation account
At first we need to enable the server user account creation with uncommenting line: 19 in
community_server/src/Controller/ServerUsersController.php
```php
$this->Auth->allow(['add', 'edit']);
```
This enable us to use this action without being logged in.
To add a signation account we need to go on the following url: http://$community_domain/server-users/add
### Coin creation process
The coin creation for work is done in the following url: http://$community_domain/transaction-creations/create-multi
Where we can create coins for a number of as many users as we want excepted for our self.
Furthermore we must sign the transactions we created. Normally after clicking on the left button (Transaktion abschließen) we should be automatically forwarded to http://$community_domain/account/checkTransactions where we can do this.
If not this page can also be reached by clicking on the shield-icon with the hook in it on the Dashboard, which is only shown if at least one transaction is waiting for signing.
For debug purposes you can check the `pending_tasks` table, which is used to store the transactions which are not signed yet or had errors.

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

View File

@ -27,7 +27,6 @@
"babel-preset-vue": "^2.0.2",
"bootstrap": "4.3.1",
"bootstrap-vue": "^2.5.0",
"chart.js": "^2.9.3",
"d3": "^5.7.0",
"datamaps": "^0.5.9",
"date-fns": "^1.30.1",
@ -64,7 +63,6 @@
"vue": "^2.6.11",
"vue-apollo": "^3.0.7",
"vue-bootstrap-typeahead": "^0.2.6",
"vue-chartjs": "^3.5.0",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-clickaway": "^2.2.2",
"vue-clipboard2": "^0.3.0",

View File

@ -1,12 +1,7 @@
<template>
<div class="decayinformation">
<span v-if="decaytyp === 'short'">
<span v-if="decay.balance > 0">
{{ decay ? ' -' + $n(decay.balance, 'decimal') + ' ' + decayStartBlockTextShort : '' }}
</span>
<span v-else>
{{ $t('decay.noDecay') }}
</span>
{{ decay ? ' - ' + $n(decay.balance, 'decimal') + ' ' + decayStartBlockTextShort : '' }}
</span>
<div v-if="decaytyp === 'new'">
@ -58,60 +53,58 @@
</b-col>
</b-row>
<div v-if="decay.balance > 0">
<!-- Decay-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col cols="6">
<div>- {{ $n(decay.balance, 'decimal') }}</div>
</b-col>
</b-row>
<hr class="mt-2 mb-2" />
<b-row>
<b-col class="text-center pt-3 pb-2">
<b>{{ $t('decay.calculation_total') }}</b>
</b-col>
</b-row>
<!-- Type-->
<b-row>
<b-col cols="6" class="text-right">
<div v-if="type === 'send'">{{ $t('decay.sent') }}</div>
<div v-if="type === 'receive'">{{ $t('decay.received') }}</div>
</b-col>
<b-col cols="6">
<div v-if="type === 'send'">- {{ $n(balance, 'decimal') }}</div>
<div v-if="type === 'receive'">+ {{ $n(balance, 'decimal') }}</div>
</b-col>
</b-row>
<!-- Decay-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col cols="6">
<div>- {{ $n(decay.balance, 'decimal') }}</div>
</b-col>
</b-row>
<!-- Total-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.total') }}</div>
</b-col>
<b-col cols="6">
<div v-if="type === 'send'">
<b>- {{ $n(balance + decay.balance, 'decimal') }}</b>
</div>
<div v-if="type === 'receive'">
<b>{{ $n(balance - decay.balance, 'decimal') }}</b>
</div>
<div v-if="type === 'creation'">
<b>- {{ $n(balance - decay.balance, 'decimal') }}</b>
</div>
</b-col>
</b-row>
</div>
<!-- Decay-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col cols="6">
<div>- {{ $n(decay.balance, 'decimal') }}</div>
</b-col>
</b-row>
<hr class="mt-2 mb-2" />
<b-row>
<b-col class="text-center pt-3 pb-2">
<b>{{ $t('decay.calculation_total') }}</b>
</b-col>
</b-row>
<!-- Type-->
<b-row>
<b-col cols="6" class="text-right">
<div v-if="type === 'send'">{{ $t('decay.sent') }}</div>
<div v-if="type === 'receive'">{{ $t('decay.received') }}</div>
</b-col>
<b-col cols="6">
<div v-if="type === 'send'">- {{ $n(balance, 'decimal') }}</div>
<div v-if="type === 'receive'">+ {{ $n(balance, 'decimal') }}</div>
</b-col>
</b-row>
<!-- Decay-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col cols="6">
<div>- {{ $n(decay.balance, 'decimal') }}</div>
</b-col>
</b-row>
<!-- Total-->
<b-row>
<b-col cols="6" class="text-right">
<div>{{ $t('decay.total') }}</div>
</b-col>
<b-col cols="6">
<div v-if="type === 'send'">
<b>- {{ $n(balance + decay.balance, 'decimal') }}</b>
</div>
<div v-if="type === 'receive'">
<b>{{ $n(balance - decay.balance, 'decimal') }}</b>
</div>
<div v-if="type === 'creation'">
<b>- {{ $n(balance - decay.balance, 'decimal') }}</b>
</div>
</b-col>
</b-row>
</div>
</div>
</template>

View File

@ -0,0 +1,45 @@
import { mount } from '@vue/test-utils'
import Status from './Status'
const localVue = global.localVue
describe('Status', () => {
let wrapper
const mocks = {
$n: jest.fn((n) => n),
}
const propsData = {
balance: 1234,
statusText: 'GDD',
}
const Wrapper = () => {
return mount(Status, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
describe('balance is pending', () => {
it('it displays an en-dash', () => {
expect(wrapper.find('div.gdd-status-div').text()).toEqual('— GDD')
})
})
describe('balance is loaded', () => {
beforeEach(() => {
wrapper.setProps({
pending: false,
})
})
it('it displays the ammount of GDD', () => {
expect(wrapper.find('div.gdd-status-div').text()).toEqual('1234 GDD')
})
})
})
})

View File

@ -0,0 +1,24 @@
<template>
<div class="gdd-status">
<div class="p-0 gdd-status-div">
{{ pending ? '—' : $n(balance, 'decimal') }} {{ statusText }}
</div>
</div>
</template>
<script>
export default {
name: 'Status',
props: {
balance: { type: Number, default: 0 },
pending: {
type: Boolean,
default: true,
},
statusText: {
type: String,
default: '',
},
},
}
</script>

View File

@ -1,8 +1,11 @@
import { mount } from '@vue/test-utils'
import Transaction from './Transaction'
import Vue from 'vue'
const localVue = global.localVue
const consoleErrorMock = jest.fn()
describe('Transaction', () => {
let wrapper
@ -27,5 +30,206 @@ describe('Transaction', () => {
it('renders the component', () => {
expect(wrapper.find('div.gdt-transaction-list-item').exists()).toBeTruthy()
})
it('has a collapse button', () => {
expect(wrapper.find('button[type="button"].btn-secondary').text()).toBe('i')
})
describe('no valid GDT entry type', () => {
beforeEach(async () => {
// disable throwing Errors on warnings to catch the warning
Vue.config.warnHandler = (w) => {}
// eslint-disable-next-line no-console
console.error = consoleErrorMock
await wrapper.setProps({ gdtEntryType: 'NOT_VALID' })
})
it('throws an error', () => {
expect(consoleErrorMock).toBeCalledWith(
expect.objectContaining({ message: 'no lines for this type: NOT_VALID' }),
)
})
})
describe('default entry type FORM', () => {
beforeEach(async () => {
await wrapper.setProps({
amount: 100,
date: '2021-05-02T17:20:11+00:00',
comment: 'This is a comment',
factor: 17,
gdt: 1700,
id: 42,
})
})
it('has the heart icon', () => {
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
})
it('has the description gdt.contribution', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.contribution')
})
it('renders the amount of euros', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('100 €')
})
it('renders the amount of GDT', () => {
expect(wrapper.findAll('div.row').at(1).text()).toContain('1700 GDT')
})
it('renders the comment message', () => {
expect(wrapper.findAll('div.row').at(2).text()).toContain('This is a comment')
})
it('renders the date', () => {
expect(wrapper.findAll('div.row').at(3).text()).toContain('Sun May 02 2021')
})
it('does not show the collapse by default', () => {
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
})
describe('without comment', () => {
it('does not render the message row', async () => {
await wrapper.setProps({ comment: undefined })
expect(wrapper.findAll('div.row').at(2).text()).toContain('form.date')
})
})
/* how to open the collapse ?????
describe('collapse is open', () => {
beforeEach(async () => {
//console.log(wrapper.html())
await wrapper.find('div#gdt-collapse-42').trigger('click')
await wrapper.vm.$nextTick()
await flushPromises()
await wrapper.vm.$nextTick()
await flushPromises()
//console.log(wrapper.find('[enteractiveclass="collapsing"]').html())
})
it('shows the collapse', () => {
//console.log(wrapper.html())
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeTruthy()
})
})
*/
})
describe('GdtEntryType.CVS', () => {
it('behaves as default FORM', async () => {
await wrapper.setProps({ gdtEntryType: 'CVS' })
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
})
})
describe('GdtEntryType.ELOPAGE', () => {
it('behaves as default FORM', async () => {
await wrapper.setProps({ gdtEntryType: 'ELOPAGE' })
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
})
})
describe('GdtEntryType.DIGISTORE', () => {
it('behaves as default FORM', async () => {
await wrapper.setProps({ gdtEntryType: 'DIGISTORE' })
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
})
})
describe('GdtEntryType.CVS2', () => {
it('behaves as default FORM', async () => {
await wrapper.setProps({ gdtEntryType: 'CVS2' })
expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy()
})
})
describe('GdtEntryType.ELOPAGE_PUBLISHER', () => {
beforeEach(async () => {
await wrapper.setProps({
amount: 365.67,
date: '2020-04-10T13:28:00+00:00',
comment: 'This is a comment',
gdtEntryType: 'ELOPAGE_PUBLISHER',
factor: 22,
gdt: 967.65,
id: 42,
})
})
it('has the person-check icon', () => {
expect(wrapper.find('svg.bi-person-check').exists()).toBeTruthy()
})
it('has the description gdt.recruited-member', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.recruited-member')
})
it('renders the percentage', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('5%')
})
it('renders the amount of GDT', () => {
expect(wrapper.findAll('div.row').at(1).text()).toContain('365.67 GDT')
})
it('renders the comment message', () => {
expect(wrapper.findAll('div.row').at(2).text()).toContain('This is a comment')
})
it('renders the date', () => {
expect(wrapper.findAll('div.row').at(3).text()).toContain('Fri Apr 10 2020')
})
it('does not show the collapse by default', () => {
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
})
describe('without comment', () => {
it('does not render the message row', async () => {
await wrapper.setProps({ comment: undefined })
expect(wrapper.findAll('div.row').at(2).text()).toContain('form.date')
})
})
})
describe('GdtEntryType.GLOBAL_MODIFICATOR', () => {
beforeEach(async () => {
await wrapper.setProps({
amount: 123.45,
date: '2020-03-12T13:28:00+00:00',
comment: 'This is a comment',
gdtEntryType: 'GLOBAL_MODIFICATOR',
factor: 19,
gdt: 61.23,
id: 42,
})
})
it('has the gift icon', () => {
expect(wrapper.find('svg.bi-gift').exists()).toBeTruthy()
})
it('has the description gdt.gdt-received', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.gdt-received')
})
it('renders the comment', () => {
expect(wrapper.findAll('div.row').at(0).text()).toContain('This is a comment')
})
it('renders the amount of GDT', () => {
expect(wrapper.findAll('div.row').at(1).text()).toContain('61.23 GDT')
})
it('renders the date', () => {
expect(wrapper.findAll('div.row').at(2).text()).toContain('Thu Mar 12 2020')
})
it('does not show the collapse by default', () => {
expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy()
})
})
})
})

View File

@ -1,13 +1,10 @@
<template>
<div>
<div class="list-group">
<div class="list-group-item gdt-transaction-list-item" v-b-toggle="'a' + date + ''">
<div class="list-group-item gdt-transaction-list-item" v-b-toggle="collapseId">
<!-- icon -->
<div class="text-right" style="position: absolute">
<b-icon
:icon="getLinesByType(gdtEntryType).icon"
:class="getLinesByType(gdtEntryType).iconclasses"
></b-icon>
<b-icon :icon="getLinesByType.icon" :class="getLinesByType.iconclasses"></b-icon>
</div>
<!-- collaps Button -->
@ -20,10 +17,10 @@
<!-- type -->
<b-row>
<b-col cols="6" class="text-right">
{{ getLinesByType(gdtEntryType).description }}
{{ getLinesByType.description }}
</b-col>
<b-col cols="6">
{{ getLinesByType(gdtEntryType).descriptiontext }}
{{ getLinesByType.descriptiontext }}
</b-col>
</b-row>
@ -33,7 +30,7 @@
{{ $t('gdt.credit') }}
</b-col>
<b-col cols="6">
{{ getLinesByType(gdtEntryType).credittext }}
{{ getLinesByType.credittext }}
</b-col>
</b-row>
@ -58,7 +55,7 @@
</b-row>
<!-- collaps trancaction info-->
<b-collapse :id="'a' + date + ''" class="mt-2 pb-4">
<b-collapse :id="collapseId" class="mt-2 pb-4">
<transaction-collapse
:amount="amount"
:gdtEntryType="gdtEntryType"
@ -86,15 +83,17 @@ export default {
gdtEntryType: { type: String, default: GdtEntryType.FORM },
factor: { type: Number },
gdt: { type: Number },
id: { type: Number },
},
computed: {
isGlobalModificator: function () {
collapseId() {
return 'gdt-collapse-' + String(this.id)
},
isGlobalModificator() {
return this.gdtEntryType === GdtEntryType.GLOBAL_MODIFICATOR
},
},
methods: {
getLinesByType(givenType) {
switch (givenType) {
getLinesByType() {
switch (this.gdtEntryType) {
case GdtEntryType.FORM:
case GdtEntryType.CVS:
case GdtEntryType.ELOPAGE:
@ -127,7 +126,7 @@ export default {
}
}
default:
throw new Error('no lines for this type: ' + givenType)
throw new Error('no lines for this type: ' + this.gdtEntryType)
}
},
},

View File

@ -2,8 +2,12 @@ import { mount } from '@vue/test-utils'
import TransactionCollapse from './TransactionCollapse'
import { GdtEntryType } from '../graphql/enums'
import Vue from 'vue'
const localVue = global.localVue
const consoleErrorMock = jest.fn()
describe('TransactionCollapse', () => {
let wrapper
@ -16,6 +20,31 @@ describe('TransactionCollapse', () => {
return mount(TransactionCollapse, { localVue, mocks, propsData })
}
describe('no valid GDT entry type', () => {
beforeEach(async () => {
// disable throwing Errors on warnings to catch the warning
Vue.config.warnHandler = (w) => {}
// eslint-disable-next-line no-console
console.error = consoleErrorMock
const propsData = {
amount: 100,
gdt: 110,
factor: 22,
gdtEntryType: GdtEntryType.FORM,
}
wrapper = Wrapper(propsData)
await wrapper.setProps({ gdtEntryType: 'NOT_VALID' })
})
it('throws an error', () => {
expect(consoleErrorMock).toBeCalledWith(
expect.objectContaining({
message: 'no additional transaction info for this type: NOT_VALID',
}),
)
})
})
describe('mount with gdtEntryType: FORM', () => {
beforeEach(() => {
const propsData = {
@ -24,7 +53,6 @@ describe('TransactionCollapse', () => {
factor: 22,
gdtEntryType: GdtEntryType.FORM,
}
wrapper = Wrapper(propsData)
})
@ -41,23 +69,23 @@ describe('TransactionCollapse', () => {
})
it('renders the component collapse-headline', () => {
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.calculation')
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.calculation')
})
it('renders the component collapse-first', () => {
expect(wrapper.find('#collapse-first').text()).toBe('gdt.factor')
expect(wrapper.find('.collapse-first').text()).toBe('gdt.factor')
})
it('renders the component collapse-second', () => {
expect(wrapper.find('#collapse-second').text()).toBe('gdt.formula')
expect(wrapper.find('.collapse-second').text()).toBe('gdt.formula')
})
it('renders the component collapse-firstMath', () => {
expect(wrapper.find('#collapse-firstMath').text()).toBe('22 GDT pro €')
expect(wrapper.find('.collapse-firstMath').text()).toBe('22 GDT pro €')
})
it('renders the component collapse-secondMath', () => {
expect(wrapper.find('#collapse-secondMath').text()).toBe('100 € * 22 GDT / € = 110 GDT')
expect(wrapper.find('.collapse-secondMath').text()).toBe('100 € * 22 GDT / € = 110 GDT')
})
})
@ -86,23 +114,23 @@ describe('TransactionCollapse', () => {
})
it('renders the component collapse-headline', () => {
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.conversion-gdt-euro')
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.conversion-gdt-euro')
})
it('renders the component collapse-first', () => {
expect(wrapper.find('#collapse-first').text()).toBe('gdt.raise')
expect(wrapper.find('.collapse-first').text()).toBe('gdt.raise')
})
it('renders the component collapse-second', () => {
expect(wrapper.find('#collapse-second').text()).toBe('gdt.conversion')
expect(wrapper.find('.collapse-second').text()).toBe('gdt.conversion')
})
it('renders the component collapse-firstMath', () => {
expect(wrapper.find('#collapse-firstMath').text()).toBe('2200 %')
expect(wrapper.find('.collapse-firstMath').text()).toBe('2200 %')
})
it('renders the component collapse-secondMath', () => {
expect(wrapper.find('#collapse-secondMath').text()).toBe('100 GDT * 2200 % = 2200 GDT')
expect(wrapper.find('.collapse-secondMath').text()).toBe('100 GDT * 2200 % = 2200 GDT')
})
})
@ -131,23 +159,23 @@ describe('TransactionCollapse', () => {
})
it('renders the component collapse-headline', () => {
expect(wrapper.find('#collapse-headline').text()).toBe('gdt.publisher')
expect(wrapper.find('.collapse-headline').text()).toBe('gdt.publisher')
})
it('renders the component collapse-first', () => {
expect(wrapper.find('#collapse-first').text()).toBe('')
expect(wrapper.find('.collapse-first').text()).toBe('')
})
it('renders the component collapse-second', () => {
expect(wrapper.find('#collapse-second').text()).toBe('')
expect(wrapper.find('.collapse-second').text()).toBe('')
})
it('renders the component collapse-firstMath', () => {
expect(wrapper.find('#collapse-firstMath').text()).toBe('')
expect(wrapper.find('.collapse-firstMath').text()).toBe('')
})
it('renders the component collapse-secondMath', () => {
expect(wrapper.find('#collapse-secondMath').text()).toBe('')
expect(wrapper.find('.collapse-secondMath').text()).toBe('')
})
})
})

View File

@ -4,19 +4,19 @@
style="border: 0px; background-color: #f1f1f1"
>
<b-row class="gdt-list-collapse-header-text text-center pb-3">
<b-col id="collapse-headline">
<b>{{ getLinesByType(gdtEntryType).headline }}</b>
<b-col class="collapse-headline">
<b>{{ getLinesByType.headline }}</b>
</b-col>
</b-row>
<b-row class="gdt-list-collapse-box--all">
<b-col cols="6" class="text-right collapse-col-left">
<div id="collapse-first">{{ getLinesByType(gdtEntryType).first }}</div>
<div id="collapse-second">{{ getLinesByType(gdtEntryType).second }}</div>
<div class="collapse-first">{{ getLinesByType.first }}</div>
<div class="collapse-second">{{ getLinesByType.second }}</div>
</b-col>
<b-col cols="6" class="collapse-col-right">
<div id="collapse-firstMath">{{ getLinesByType(gdtEntryType).firstMath }}</div>
<div id="collapse-secondMath">
{{ getLinesByType(gdtEntryType).secondMath }}
<div class="collapse-firstMath">{{ getLinesByType.firstMath }}</div>
<div class="collapse-secondMath">
{{ getLinesByType.secondMath }}
</div>
</b-col>
</b-row>
@ -33,9 +33,9 @@ export default {
factor: { type: Number },
gdt: { type: Number },
},
methods: {
getLinesByType(givenType) {
switch (givenType) {
computed: {
getLinesByType() {
switch (this.gdtEntryType) {
case GdtEntryType.FORM:
case GdtEntryType.CVS:
case GdtEntryType.ELOPAGE:
@ -80,7 +80,7 @@ export default {
}
}
default:
throw new Error('no additional transaction info for this type: ' + givenType)
throw new Error('no additional transaction info for this type: ' + this.gdtEntryType)
}
},
},

View File

@ -96,9 +96,10 @@
"conversion-gdt-euro": "Umrechnung Euro / Gradido Transform (GDT)",
"credit": "Gutschrift",
"factor": "Faktor",
"formula": "Berechungsformel",
"formula": "Berechnungsformel",
"funding": "Zu den Förderbeiträgen",
"gdt-received": "Gradido Transform (GDT) erhalten",
"no-transactions": "Du hast zur Zeit keine Transaktionen",
"no-transactions": "Du hast noch keine Gradido Transform (GDT).",
"publisher": "Dein geworbenes Mitglied hat einen Beitrag bezahlt",
"raise": "Erhöhung",
"recruited-member": "Geworbenes Mitglied"
@ -109,6 +110,7 @@
"logout": "Abmelden",
"members_area": "Mitgliederbereich",
"message": "hallo gradido !!",
"overview": "Übersicht",
"privacy_policy": "Datenschutzerklärung",
"send": "Senden",
"settings": {
@ -130,8 +132,8 @@
},
"newsletter": {
"newsletter": "Newsletter",
"newsletterFalse": "Du bist aus Newslettersystem ausgetragen.",
"newsletterTrue": "Du bist im Newslettersystem eingetraten."
"newsletterFalse": "Du erhältst keine Informationen per E-Mail.",
"newsletterTrue": "Du erhältst Informationen per E-Mail."
},
"password": {
"change-password": "Passwort ändern",

View File

@ -97,8 +97,9 @@
"credit": "Credit",
"factor": "Factor",
"formula": "Calculation formula",
"funding": "Regarding the funding contributions",
"gdt-received": "Gradido Transform (GDT) received",
"no-transactions": "You currently have no transactions",
"no-transactions": "You do not have Gradido Transform (GDT) yet.",
"publisher": "A member you referred has paid a contribution",
"raise": "Increase",
"recruited-member": "Recruited Member"
@ -109,6 +110,7 @@
"logout": "Logout",
"members_area": "Member's area",
"message": "hello gradido !!",
"overview": "Overview",
"privacy_policy": "Privacy policy",
"send": "Send",
"settings": {
@ -130,8 +132,8 @@
},
"newsletter": {
"newsletter": "Newsletter",
"newsletterFalse": "You are unsubscribed from newsletter system.",
"newsletterTrue": "You are subscribed to newsletter system."
"newsletterFalse": "You will not receive any information by e-mail.",
"newsletterTrue": "You will receive information by e-mail."
},
"password": {
"change-password": "Change password",

View File

@ -6,6 +6,7 @@ import { loadAllRules } from './validation-rules'
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo'
import CONFIG from './config'
import addNavigationGuards from './routes/guards'
import { store } from './store/store'

View File

@ -49,8 +49,8 @@ describe('router', () => {
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
})
it('has thirteen routes defined', () => {
expect(routes).toHaveLength(13)
it('has fourteen routes defined', () => {
expect(routes).toHaveLength(14)
})
describe('overview', () => {
@ -64,6 +64,17 @@ describe('router', () => {
})
})
describe('send', () => {
it('requires authorization', () => {
expect(routes.find((r) => r.path === '/send').meta.requiresAuth).toBeTruthy()
})
it('loads the "Send" component', async () => {
const component = await routes.find((r) => r.path === '/send').component()
expect(component.default.name).toBe('SendOverview')
})
})
describe('profile', () => {
it('requires authorization', () => {
expect(routes.find((r) => r.path === '/profile').meta.requiresAuth).toBeTruthy()

View File

@ -14,6 +14,13 @@ const routes = [
requiresAuth: true,
},
},
{
path: '/send',
component: () => import('../views/Pages/SendOverview.vue'),
meta: {
requiresAuth: true,
},
},
{
path: '/profile',
component: () => import('../views/Pages/UserProfileOverview.vue'),

View File

@ -4,6 +4,8 @@ import DashboardLayoutGdd from './DashboardLayout_gdd'
jest.useFakeTimers()
jest.setTimeout(30000)
const localVue = global.localVue
const storeDispatchMock = jest.fn()
@ -87,33 +89,40 @@ describe('DashboardLayoutGdd', () => {
navbar = wrapper.findAll('ul.navbar-nav').at(0)
})
it('has three items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(3)
it('has four items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(4)
})
it('has first item "send" in navbar', () => {
expect(navbar.findAll('ul > a').at(0).text()).toEqual('send')
it('has first item "overview" in navbar', () => {
expect(navbar.findAll('ul > a').at(0).text()).toEqual('overview')
})
it('has first item "send" linked to overview in navbar', () => {
navbar.findAll('ul > a').at(0).trigger('click')
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/overview')
it('has first item "overview" linked to overview in navbar', () => {
expect(navbar.findAll('ul > a > a').at(0).attributes('href')).toBe('/overview')
})
it('has second item "transactions" in navbar', () => {
expect(navbar.findAll('ul > a').at(1).text()).toEqual('transactions')
it('has second item "send" in navbar', () => {
expect(navbar.findAll('ul > a').at(1).text()).toEqual('send')
})
it('has second item "transactions" linked to transactions in navbar', async () => {
expect(wrapper.findAll('a').at(3).attributes('href')).toBe('/transactions')
it('has second item "send" linked to /send in navbar', () => {
expect(wrapper.findAll('ul > a > a').at(1).attributes('href')).toBe('/send')
})
it('has three items in the navbar', () => {
expect(navbar.findAll('ul > a')).toHaveLength(3)
it('has third item "transactions" in navbar', () => {
expect(navbar.findAll('ul > a').at(2).text()).toEqual('transactions')
})
it('has third item "My profile" linked to profile in navbar', async () => {
expect(wrapper.findAll('a').at(5).attributes('href')).toBe('/profile')
it('has third item "transactions" linked to transactions in navbar', async () => {
expect(wrapper.findAll('ul > a > a').at(2).attributes('href')).toBe('/transactions')
})
it('has fourth item "My profile" in navbar', () => {
expect(navbar.findAll('ul > a').at(3).text()).toEqual('site.navbar.my-profil')
})
it('has fourth item "My profile" linked to profile in navbar', async () => {
expect(wrapper.findAll('ul > a > a').at(3).attributes('href')).toBe('/profile')
})
it('has a link to the members area', () => {
@ -137,6 +146,7 @@ describe('DashboardLayoutGdd', () => {
})
await wrapper.findComponent({ name: 'sidebar' }).vm.$emit('logout')
await flushPromises()
await wrapper.vm.$nextTick()
})
it('calls the API', async () => {
@ -168,6 +178,17 @@ describe('DashboardLayoutGdd', () => {
it('redirects to login page', () => {
expect(routerPushMock).toBeCalledWith('/login')
})
describe('redirect to login already done', () => {
beforeEach(() => {
mocks.$router.currentRoute.path = '/login'
jest.resetAllMocks()
})
it('does not call the redirect to login', () => {
expect(routerPushMock).not.toBeCalled()
})
})
})
describe('update balance', () => {

View File

@ -2,11 +2,16 @@
<div>
<side-bar @logout="logout" :balance="balance" :pending="pending">
<template slot="links">
<p></p>
<sidebar-item
:link="{
name: $t('overview'),
path: '/overview',
}"
></sidebar-item>
<sidebar-item
:link="{
name: $t('send'),
path: '/overview',
path: '/send',
}"
></sidebar-item>
<sidebar-item

View File

@ -1,9 +1,6 @@
import { mount } from '@vue/test-utils'
import AccountOverview from './AccountOverview'
const sendMock = jest.fn()
sendMock.mockResolvedValue('success')
const localVue = global.localVue
window.scrollTo = jest.fn()
@ -11,26 +8,16 @@ window.scrollTo = jest.fn()
describe('AccountOverview', () => {
let wrapper
const propsData = {
balance: 123.45,
transactionCount: 1,
}
const mocks = {
$t: jest.fn((t) => t),
$n: jest.fn((n) => String(n)),
$store: {
state: {
email: 'sender@example.org',
},
},
$apollo: {
mutate: sendMock,
},
$n: jest.fn(),
}
const Wrapper = () => {
return mount(AccountOverview, { localVue, mocks, propsData })
return mount(AccountOverview, {
localVue,
mocks,
})
}
describe('mount', () => {
@ -38,97 +25,21 @@ describe('AccountOverview', () => {
wrapper = Wrapper()
})
it('has a status line', () => {
expect(wrapper.find('div.gdd-status').exists()).toBeTruthy()
it('has a status gdd-status-gdd', () => {
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
})
it('has a send field', () => {
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
it('has a status gdd-status-gdt', () => {
expect(wrapper.find('div.gdd-status-gdt').exists()).toBeTruthy()
})
it('has a transactions table', () => {
expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
})
describe('transaction form', () => {
it('steps forward in the dialog', async () => {
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
})
})
describe('confirm transaction', () => {
beforeEach(() => {
wrapper.setData({
currentTransactionStep: 1,
transactionData: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
})
})
it('resets the transaction process when on-reset is emitted', async () => {
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
expect(wrapper.vm.transactionData).toEqual({
email: '',
amount: 0,
memo: '',
})
})
describe('transaction is confirmed and server response is success', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('calls the API when send-transaction is emitted', async () => {
expect(sendMock).toBeCalledWith(
expect.objectContaining({
variables: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
}),
)
})
it('emits update-balance', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy()
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
})
it('shows the succes page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
})
})
describe('transaction is confirmed and server response is error', () => {
beforeEach(async () => {
jest.clearAllMocks()
sendMock.mockRejectedValue({ message: 'receiver not found' })
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('shows the error page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
})
it('shows recipient not found', () => {
expect(wrapper.text()).toContain('transaction.receiverNotFound')
})
describe('timestamp updates', () => {
it('emits update transactions', async () => {
expect(wrapper.emitted('update-transactions')).toHaveLength(1)
await wrapper.setData({ timestamp: Date.now() })
expect(wrapper.emitted('update-transactions')).toHaveLength(2)
})
})
})

View File

@ -1,83 +1,59 @@
<template>
<div>
<b-container fluid>
<gdd-status
v-if="showContext"
:pending="pending"
:balance="balance"
:gdt-balance="GdtBalance"
/>
<b-row>
<b-col class="col-6">
<b-row>
<b-col class="col-11 bg-gray text-white p-3">
<status
class="gdd-status-gdd"
:pending="pending"
:balance="balance"
status-text="GDD"
/>
</b-col>
</b-row>
</b-col>
<b-col class="col-6 text-right">
<b-row>
<b-col class="bg-white text-gray p-3">
<status
class="gdd-status-gdt"
:pending="pending"
:balance="GdtBalance"
status-text="GDT"
/>
</b-col>
</b-row>
</b-col>
</b-row>
<br />
<gdd-send :currentTransactionStep="currentTransactionStep">
<template #transaction-form>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
</template>
<template #transaction-confirmation>
<transaction-confirmation
:email="transactionData.email"
:amount="transactionData.amount"
:memo="transactionData.memo"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
></transaction-confirmation>
</template>
<template #transaction-result>
<transaction-result
:error="error"
:errorResult="errorResult"
@on-reset="onReset"
></transaction-result>
</template>
</gdd-send>
<hr />
<gdd-transaction-list
v-if="showContext"
:transactions="transactions"
:pageSize="5"
:timestamp="timestamp"
:transaction-count="transactionCount"
@update-transactions="updateTransactions"
/>
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
<gdd-transaction-list-footer :count="transactionCount" />
</b-container>
</div>
</template>
<script>
import GddStatus from './AccountOverview/GddStatus.vue'
import GddSend from './AccountOverview/GddSend.vue'
import Status from '../../components/Status.vue'
import GddTransactionList from './AccountOverview/GddTransactionList.vue'
import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter.vue'
import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue'
import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue'
import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue'
import { sendCoins } from '../../graphql/mutations.js'
const EMPTY_TRANSACTION_DATA = {
email: '',
amount: 0,
memo: '',
}
export default {
name: 'Overview',
components: {
GddStatus,
GddSend,
Status,
GddTransactionList,
GddTransactionListFooter,
TransactionForm,
TransactionConfirmation,
TransactionResult,
},
data() {
return {
timestamp: Date.now(),
transactionData: { ...EMPTY_TRANSACTION_DATA },
error: false,
errorResult: '',
currentTransactionStep: 0,
loading: false,
}
},
props: {
@ -92,38 +68,7 @@ export default {
default: true,
},
},
computed: {
showContext() {
return this.currentTransactionStep === 0
},
},
methods: {
setTransaction(data) {
this.transactionData = { ...data }
this.currentTransactionStep = 1
},
async sendTransaction() {
this.loading = true
this.$apollo
.mutate({
mutation: sendCoins,
variables: this.transactionData,
})
.then(() => {
this.error = false
this.$emit('update-balance', this.transactionData.amount)
})
.catch((err) => {
this.errorResult = err.message
this.error = true
})
this.currentTransactionStep = 2
this.loading = false
},
onReset() {
this.transactionData = { ...EMPTY_TRANSACTION_DATA }
this.currentTransactionStep = 0
},
updateTransactions(pagination) {
this.$emit('update-transactions', pagination)
},

View File

@ -1,53 +0,0 @@
import { mount } from '@vue/test-utils'
import GddStatus from './GddStatus'
const localVue = global.localVue
describe('GddStatus', () => {
let wrapper
const mocks = {
$n: jest.fn((n) => n),
}
const propsData = {
balance: 1234,
GdtBalance: 9876,
}
const Wrapper = () => {
return mount(GddStatus, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
describe('balance is loading', () => {
it('it displays em-dash as the ammount of GDD', () => {
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('— GDD')
})
it('it displays em-dash as the ammount of GDT', () => {
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('— GDT')
})
})
describe('balance is loaded', () => {
beforeEach(() => {
wrapper.setProps({
pending: false,
})
})
it('it displays the ammount of GDD', () => {
expect(wrapper.findAll('div.card-body').at(0).text()).toEqual('1234 GDD')
})
it('it displays the ammount of GDT', () => {
expect(wrapper.findAll('div.card-body').at(1).text()).toEqual('9876 GDT')
})
})
})
})

View File

@ -1,30 +0,0 @@
<template>
<div class="gdd-status">
<b-row>
<b-col class="p-0">
<b-card class="p-0" style="background-color: #ebebeba3 !important">
{{ pending ? '—' : $n(balance, 'decimal') }} GDD
</b-card>
</b-col>
<b-col class="pr-0">
<b-card class="p-0 text-right" style="background-color: #ebebeba3 !important">
{{ pending ? '—' : $n(GdtBalance, 'decimal') }} GDT
</b-card>
</b-col>
</b-row>
</div>
</template>
<script>
export default {
name: 'GddStatus',
props: {
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
pending: {
type: Boolean,
default: true,
},
},
}
</script>

View File

@ -138,7 +138,7 @@ describe('GddTransactionList', () => {
it('shows the decay calculation', () => {
expect(transaction.findAll('div.gdd-transaction-list-item-decay').at(0).text()).toContain(
'-0.5',
'- 0.5',
)
})
})
@ -228,7 +228,7 @@ describe('GddTransactionList', () => {
it('shows the decay calculation', () => {
expect(transaction.findAll('.gdd-transaction-list-item-decay').at(0).text()).toContain(
'-1.5',
'- 1.5',
)
})
})

View File

@ -124,7 +124,7 @@
:per-page="pageSize"
:total-rows="transactionCount"
></pagination-buttons>
<div v-if="transactions.length === 0" class="mt-4 text-center">
<div v-if="transactionCount === 0" class="mt-4 text-center">
<span>{{ $t('transaction.nullTransactions') }}</span>
</div>
</div>
@ -198,4 +198,8 @@ export default {
padding-left: 0px;
padding-right: 0px;
}
.gdd-transaction-list-item {
outline: none !important;
}
</style>

View File

@ -7,41 +7,8 @@ const localVue = global.localVue
const apolloMock = jest.fn().mockResolvedValue({
data: {
listGDTEntries: {
count: 4,
gdtEntries: [
{
amount: 100,
gdt: 1700,
factor: 17,
comment: '',
date: '2021-05-02T17:20:11+00:00',
gdtEntryType: GdtEntryType.FORM,
},
{
amount: 1810,
gdt: 362,
factor: 0.2,
comment: 'Dezember 20',
date: '2020-12-31T12:00:00+00:00',
gdtEntryType: GdtEntryType.GLOBAL_MODIFICATOR,
},
{
amount: 100,
gdt: 1700,
factor: 17,
comment: '',
date: '2020-05-07T17:00:00+00:00',
gdtEntryType: GdtEntryType.FORM,
},
{
amount: 100,
gdt: 110,
factor: 22,
comment: '',
date: '2020-04-10T13:28:00+00:00',
gdtEntryType: GdtEntryType.ELOPAGE_PUBLISHER,
},
],
count: 0,
gdtEntries: [],
},
},
})
@ -51,10 +18,18 @@ const windowScrollToMock = jest.fn()
window.scrollTo = windowScrollToMock
describe('GdtTransactionList', () => {
const state = {
language: 'en',
}
describe('GdtTransactionList ', () => {
let wrapper
const mocks = {
$store: {
state,
commit: jest.fn(),
},
$i18n: {
locale: 'en',
},
@ -73,15 +48,80 @@ describe('GdtTransactionList', () => {
return mount(GdtTransactionList, { localVue, mocks })
}
describe('mount', () => {
describe('mount - When no transactions are loaded', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the funding button ', () => {
expect(wrapper.find('.gdt-funding').exists()).toBe(true)
})
it('links to https://gradido.net/en/memberships/ when clicking', async () => {
expect(wrapper.find('.gdt-funding').attributes('href')).toBe(
'https://gradido.net/' + state.language + '/memberships/',
)
})
})
describe('mount - When transactions are loaded', () => {
beforeEach(() => {
apolloMock.mockResolvedValue({
data: {
listGDTEntries: {
count: 4,
gdtEntries: [
{
id: 1,
amount: 100,
gdt: 1700,
factor: 17,
comment: '',
date: '2021-05-02T17:20:11+00:00',
gdtEntryType: GdtEntryType.FORM,
},
{
id: 2,
amount: 1810,
gdt: 362,
factor: 0.2,
comment: 'Dezember 20',
date: '2020-12-31T12:00:00+00:00',
gdtEntryType: GdtEntryType.GLOBAL_MODIFICATOR,
},
{
id: 3,
amount: 100,
gdt: 1700,
factor: 17,
comment: '',
date: '2020-05-07T17:00:00+00:00',
gdtEntryType: GdtEntryType.FORM,
},
{
id: 4,
amount: 100,
gdt: 110,
factor: 22,
comment: '',
date: '2020-04-10T13:28:00+00:00',
gdtEntryType: GdtEntryType.ELOPAGE_PUBLISHER,
},
],
},
},
})
wrapper = Wrapper()
})
it('renders the component', () => {
expect(wrapper.find('div.gdt-transaction-list').exists()).toBeTruthy()
})
it('does not render the funding button ', () => {
expect(wrapper.find('.gdt-funding').exists()).toBe(false)
})
describe('server returns valid data', () => {
it('calls the API', async () => {
await wrapper.vm.$nextTick()

View File

@ -1,21 +1,17 @@
<template>
<div class="gdt-transaction-list">
<div class="list-group" style="background-color: #fff">
<div v-if="transactionGdtCount === 0">
<div class="list-group">
<div v-if="transactionGdtCount === 0" class="text-center">
{{ $t('gdt.no-transactions') }}
<hr />
<b-button class="gdt-funding" :href="link" target="_blank">
{{ $t('gdt.funding') }}
</b-button>
</div>
<div
v-else
v-for="{
transactionId,
amount,
date,
comment,
gdtEntryType,
factor,
gdt,
} in transactionsGdt"
:key="transactionId"
v-for="{ id, amount, date, comment, gdtEntryType, factor, gdt } in transactionsGdt"
:key="id"
>
<transaction
:amount="amount"
@ -24,6 +20,7 @@
:gdtEntryType="gdtEntryType"
:factor="factor"
:gdt="gdt"
:id="id"
></transaction>
</div>
</div>
@ -52,6 +49,7 @@ export default {
transactionGdtCount: { type: Number, default: 0 },
currentPage: 1,
pageSize: 25,
link: 'https://gradido.net/' + this.$store.state.language + '/memberships/',
}
},
methods: {
@ -97,4 +95,8 @@ export default {
.nav-tabs .nav-item.show .nav-link {
background-color: #f8f9fe38;
}
.gdt-transaction-list-item {
outline: none !important;
}
</style>

View File

@ -5,10 +5,10 @@
<div class="header-body text-center mb-7">
<b-row class="justify-content-center">
<b-col xl="5" lg="6" md="8" class="px-2">
<h1>{{ $t('checkEmail.title') }}</h1>
<h1>{{ $t('site.checkEmail.title') }}</h1>
<div class="pb-4" v-if="!pending">
<span v-if="!authenticated">
{{ $t('checkEmail.errorText') }}
{{ $t('site.checkEmail.errorText') }}
</span>
</div>
</b-col>

View File

@ -205,12 +205,6 @@ export default {
},
})
.then(() => {
this.form.email = ''
this.form.firstname = ''
this.form.lastname = ''
this.form.password.password = ''
this.form.password.passwordRepeat = ''
this.language = ''
this.$router.push('/thx/register')
})
.catch((error) => {
@ -226,7 +220,6 @@ export default {
this.form.lastname = ''
this.form.password.password = ''
this.form.password.passwordRepeat = ''
this.language = ''
},
},
computed: {

View File

@ -0,0 +1,135 @@
import { mount } from '@vue/test-utils'
import SendOverview from './SendOverview'
const sendMock = jest.fn()
sendMock.mockResolvedValue('success')
const localVue = global.localVue
// window.scrollTo = jest.fn()
describe('SendOverview', () => {
let wrapper
const propsData = {
balance: 123.45,
transactionCount: 1,
}
const mocks = {
$t: jest.fn((t) => t),
$n: jest.fn((n) => String(n)),
$store: {
state: {
email: 'sender@example.org',
},
},
$apollo: {
mutate: sendMock,
},
}
const Wrapper = () => {
return mount(SendOverview, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a status GDD line gdd-status-gdd', () => {
expect(wrapper.find('div.gdd-status-gdd').exists()).toBeTruthy()
})
it('has a send field', () => {
expect(wrapper.find('div.gdd-send').exists()).toBeTruthy()
})
// it('has a transactions table', () => {
// expect(wrapper.find('div.gdd-transaction-list').exists()).toBeTruthy()
// })
describe('transaction form', () => {
it('steps forward in the dialog', async () => {
await wrapper.findComponent({ name: 'TransactionForm' }).vm.$emit('set-transaction', {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
expect(wrapper.findComponent({ name: 'TransactionConfirmation' }).exists()).toBeTruthy()
})
})
describe('confirm transaction', () => {
beforeEach(() => {
wrapper.setData({
currentTransactionStep: 1,
transactionData: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
})
})
it('resets the transaction process when on-reset is emitted', async () => {
await wrapper.findComponent({ name: 'TransactionConfirmation' }).vm.$emit('on-reset')
expect(wrapper.findComponent({ name: 'TransactionForm' }).exists()).toBeTruthy()
expect(wrapper.vm.transactionData).toEqual({
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
})
})
describe('transaction is confirmed and server response is success', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('calls the API when send-transaction is emitted', async () => {
expect(sendMock).toBeCalledWith(
expect.objectContaining({
variables: {
email: 'user@example.org',
amount: 23.45,
memo: 'Make the best of it!',
},
}),
)
})
it('emits update-balance', () => {
expect(wrapper.emitted('update-balance')).toBeTruthy()
expect(wrapper.emitted('update-balance')).toEqual([[23.45]])
})
it('shows the succes page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_success')
})
})
describe('transaction is confirmed and server response is error', () => {
beforeEach(async () => {
jest.clearAllMocks()
sendMock.mockRejectedValue({ message: 'receiver not found' })
await wrapper
.findComponent({ name: 'TransactionConfirmation' })
.vm.$emit('send-transaction')
})
it('shows the error page', () => {
expect(wrapper.find('div.card-body').text()).toContain('form.send_transaction_error')
})
it('shows recipient not found', () => {
expect(wrapper.text()).toContain('transaction.receiverNotFound')
})
})
})
})
})

View File

@ -0,0 +1,122 @@
<template>
<div>
<b-container fluid>
<b-row>
<b-col class="bg-gray text-white text-center p-3">
<status
class="gdd-status-gdd"
v-if="showContext"
:pending="pending"
:balance="balance"
status-text="GDD"
/>
</b-col>
</b-row>
<br />
<gdd-send :currentTransactionStep="currentTransactionStep">
<template #transaction-form>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
</template>
<template #transaction-confirmation>
<transaction-confirmation
:email="transactionData.email"
:amount="transactionData.amount"
:memo="transactionData.memo"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
></transaction-confirmation>
</template>
<template #transaction-result>
<transaction-result
:error="error"
:errorResult="errorResult"
@on-reset="onReset"
></transaction-result>
</template>
</gdd-send>
<hr />
</b-container>
</div>
</template>
<script>
import Status from '../../components/Status.vue'
import GddSend from './SendOverview/GddSend.vue'
import TransactionForm from './SendOverview/GddSend/TransactionForm.vue'
import TransactionConfirmation from './SendOverview/GddSend/TransactionConfirmation.vue'
import TransactionResult from './SendOverview/GddSend/TransactionResult.vue'
import { sendCoins } from '../../graphql/mutations.js'
const EMPTY_TRANSACTION_DATA = {
email: '',
amount: 0,
memo: '',
}
export default {
name: 'SendOverview',
components: {
Status,
GddSend,
TransactionForm,
TransactionConfirmation,
TransactionResult,
},
data() {
return {
transactionData: { ...EMPTY_TRANSACTION_DATA },
error: false,
errorResult: '',
currentTransactionStep: 0,
loading: false,
}
},
props: {
balance: { type: Number, default: 0 },
GdtBalance: { type: Number, default: 0 },
transactions: {
default: () => [],
},
pending: {
type: Boolean,
default: true,
},
},
computed: {
showContext() {
return this.currentTransactionStep === 0
},
},
methods: {
setTransaction(data) {
this.transactionData = { ...data }
this.currentTransactionStep = 1
},
async sendTransaction() {
this.loading = true
this.$apollo
.mutate({
mutation: sendCoins,
variables: this.transactionData,
})
.then(() => {
this.error = false
this.$emit('update-balance', this.transactionData.amount)
})
.catch((err) => {
this.errorResult = err.message
this.error = true
})
this.currentTransactionStep = 2
this.loading = false
},
onReset() {
this.currentTransactionStep = 0
},
},
}
</script>

View File

@ -62,4 +62,8 @@ export default {
background-color: aquamarine;
font-size: larger;
}
.nav-tabs > li > a {
outline: none !important;
}
</style>

View File

@ -1735,13 +1735,6 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/chart.js@^2.7.55":
version "2.9.30"
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.30.tgz#34b99897f4f5ef0f74c8fe4ced70ac52b4d752dd"
integrity sha512-EgjxUUZFvf6ls3kW2CwyrnSJhgyKxgwrlp/W5G9wqyPEO9iFatO63zAA7L24YqgMxiDjQ+tG7ODU+2yWH91lPg==
dependencies:
moment "^2.10.2"
"@types/d3@3.5.38":
version "3.5.38"
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.38.tgz#76f8f2e9159ae562965b2fa0e6fbee1aa643a1bc"
@ -3823,29 +3816,6 @@ chardet@^0.4.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
chart.js@^2.9.3:
version "2.9.4"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
chartjs-color-string@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
dependencies:
color-name "^1.0.0"
chartjs-color@^2.1.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
dependencies:
chartjs-color-string "^0.6.0"
color-convert "^1.9.3"
check-types@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@ -4073,7 +4043,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
color-convert@^1.9.0, color-convert@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@ -9620,7 +9590,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment@^2.10.2, moment@^2.19.2:
moment@^2.19.2:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
@ -13469,13 +13439,6 @@ vue-bootstrap-typeahead@^0.2.6:
resize-observer-polyfill "^1.5.0"
vue "^2.5.17"
vue-chartjs@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-3.5.1.tgz#d25e845708f7744ae51bed9d23a975f5f8fc6529"
integrity sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==
dependencies:
"@types/chart.js" "^2.7.55"
vue-cli-plugin-i18n@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-1.0.1.tgz#5a3077de5d62c9b4068e486db1fc97fce9fa0072"

View File

@ -5,6 +5,5 @@ src/cpsp/*.h
src/cpsp/*.cpp
src/cpp/proto/
build*/
/skeema/gradido_login/insert/crypto_key.sql
src/LOCALE/messages.pot

View File

@ -56,8 +56,7 @@ To update messages.pot run
This will be also called by ./scripts/build_debug.sh
## database
Login-Server needs a db to run, it is tested with mariadb
table definitions are found in folder ./skeema/gradido_login
Login-Server needs a db to run, it is tested with mariadb.
Currently at least one group must be present in table groups.
For example:
```sql

View File

@ -1,9 +0,0 @@
CREATE TABLE `app_access_tokens` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`access_code` bigint unsigned NOT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `access_code` (`access_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -1,15 +0,0 @@
CREATE TABLE `elopage_buys` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`elopage_user_id` int DEFAULT NULL,
`affiliate_program_id` int NOT NULL,
`publisher_id` int NOT NULL,
`order_id` int NOT NULL,
`product_id` int NOT NULL,
`product_price` int NOT NULL,
`payer_email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`publisher_email` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`payed` tinyint NOT NULL,
`success_date` datetime NOT NULL,
`event` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Some files were not shown because too many files have changed in this diff Show More