mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge pull request #3207 from gradido/iota7.1_add_community
feat(dlt): add community, use dlt-database
This commit is contained in:
commit
abe54d420f
18
.github/workflows/test_dlt_connector.yml
vendored
18
.github/workflows/test_dlt_connector.yml
vendored
@ -60,15 +60,13 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-dlt-connector-test
|
||||
path: /tmp
|
||||
|
||||
- name: DLT-Connector | docker-compose mariadb
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/dlt-connector.tar
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
shell: bash
|
||||
|
||||
- name: Unit tests
|
||||
run: docker run --env NODE_ENV=test --rm gradido/dlt-connector:test yarn run test
|
||||
- name: DLT-Connector | Unit tests
|
||||
run: cd dlt-database && yarn && yarn build && cd ../dlt-connector && yarn && yarn test
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
CONFIG_VERSION=v2.2023-07-07
|
||||
CONFIG_VERSION=v4.2023-09-12
|
||||
|
||||
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
||||
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
||||
@ -7,6 +7,16 @@ CONFIG_VERSION=v2.2023-07-07
|
||||
# IOTA
|
||||
IOTA_API_URL=https://chrysalis-nodes.iota.org
|
||||
IOTA_COMMUNITY_ALIAS=GRADIDO: TestHelloWelt2
|
||||
IOTA_HOME_COMMUNITY_SEED=aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_dlt
|
||||
DB_DATABASE_TEST=gradido_dlt_test
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=6010
|
||||
@ -3,6 +3,16 @@ CONFIG_VERSION=$DLT_CONNECTOR_CONFIG_VERSION
|
||||
#IOTA
|
||||
IOTA_API_URL=$IOTA_API_URL
|
||||
IOTA_COMMUNITY_ALIAS=$IOTA_COMMUNITY_ALIAS
|
||||
IOTA_HOME_COMMUNITY_SEED=$IOTA_HOME_COMMUNITY_SEED
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_dlt
|
||||
DB_DATABASE_TEST=$DB_DATABASE_TEST
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT
|
||||
@ -44,6 +44,7 @@ EXPOSE ${PORT}
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
RUN mkdir -p /dlt-database
|
||||
|
||||
##################################################################################
|
||||
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
||||
@ -56,7 +57,7 @@ FROM base as development
|
||||
# Run command
|
||||
# (for development we need to execute yarn install since the
|
||||
# node_modules are on another volume and need updating)
|
||||
CMD /bin/sh -c "cd /app && yarn install && yarn run dev"
|
||||
CMD /bin/sh -c "cd /dlt-database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
|
||||
|
||||
##################################################################################
|
||||
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||
@ -65,13 +66,21 @@ FROM base as build
|
||||
|
||||
# Copy everything from dlt-connector
|
||||
COPY ./dlt-connector/ ./
|
||||
# Copy everything from dlt-database
|
||||
COPY ./dlt-database/ ../dlt-database/
|
||||
|
||||
# yarn install dlt-connector
|
||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||
|
||||
# yarn install dlt-database
|
||||
RUN cd ../dlt-database && yarn install --production=false --frozen-lockfile --non-interactive
|
||||
|
||||
# yarn build
|
||||
RUN yarn run build
|
||||
|
||||
# yarn build dlt-database
|
||||
RUN cd ../dlt-database && yarn run build
|
||||
|
||||
##################################################################################
|
||||
# TEST ###########################################################################
|
||||
##################################################################################
|
||||
@ -90,9 +99,10 @@ RUN apk del rust cargo python3 make g++
|
||||
|
||||
# Copy "binary"-files from build image
|
||||
COPY --from=build ${DOCKER_WORKDIR}/build ./build
|
||||
|
||||
COPY --from=build ${DOCKER_WORKDIR}/../dlt-database/build ../dlt-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}/../dlt-database/node_modules ../dlt-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)
|
||||
|
||||
@ -21,17 +21,18 @@ module.exports = {
|
||||
'@input/(.*)': '<rootDir>/src/graphql/input/$1',
|
||||
'@proto/(.*)': '<rootDir>/src/proto/$1',
|
||||
'@test/(.*)': '<rootDir>/test/$1',
|
||||
'@typeorm/(.*)': '<rootDir>/src/typeorm/$1',
|
||||
'@client/(.*)': '<rootDir>/src/client/$1',
|
||||
'@entity/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/entity/$1'
|
||||
: '<rootDir>/../database/build/entity/$1',
|
||||
? '<rootDir>/../dlt-database/entity/$1'
|
||||
: '<rootDir>/../dlt-database/build/entity/$1',
|
||||
'@dbTools/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/src/$1'
|
||||
: '<rootDir>/../database/build/src/$1',
|
||||
? '<rootDir>/../dlt-database/src/$1'
|
||||
: '<rootDir>/../dlt-database/build/src/$1',
|
||||
'@validator/(.*)': '<rootDir>/src/graphql/validator/$1',
|
||||
},
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"dlt-database": "file:../dlt-database",
|
||||
"dotenv": "10.0.0",
|
||||
"express": "4.17.1",
|
||||
"graphql": "^16.7.1",
|
||||
@ -32,6 +33,7 @@
|
||||
"log4js": "^6.7.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sodium-native": "^4.0.4",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"type-graphql": "^2.0.0-beta.2"
|
||||
},
|
||||
@ -41,6 +43,7 @@
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/sodium-native": "^2.3.5",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
@ -58,6 +61,8 @@
|
||||
"prettier": "^2.8.7",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"typeorm": "^0.3.17",
|
||||
"typeorm-extension": "^3.0.1",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -4,11 +4,12 @@ dotenv.config()
|
||||
|
||||
const constants = {
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
DB_VERSION: '0002-refactor_add_community',
|
||||
// default log level on production should be info
|
||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v2.2023-07-07',
|
||||
EXPECTED: 'v4.2023-09-12',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
@ -17,9 +18,20 @@ const server = {
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_HOST: process.env.DB_HOST ?? 'localhost',
|
||||
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
|
||||
DB_USER: process.env.DB_USER ?? 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD ?? '',
|
||||
DB_DATABASE: process.env.DB_DATABASE ?? 'gradido_dlt',
|
||||
DB_DATABASE_TEST: process.env.DB_DATABASE_TEST ?? 'gradido_dlt_test',
|
||||
TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH ?? 'typeorm.backend.log',
|
||||
}
|
||||
|
||||
const iota = {
|
||||
IOTA_API_URL: process.env.IOTA_API_URL ?? 'https://chrysalis-nodes.iota.org',
|
||||
IOTA_COMMUNITY_ALIAS: process.env.IOTA_COMMUNITY_ALIAS ?? 'GRADIDO: TestHelloWelt2',
|
||||
IOTA_HOME_COMMUNITY_SEED: process.env.IOTA_HOME_COMMUNITY_SEED ?? null,
|
||||
}
|
||||
|
||||
const dltConnector = {
|
||||
@ -41,6 +53,7 @@ if (
|
||||
export const CONFIG = {
|
||||
...constants,
|
||||
...server,
|
||||
...database,
|
||||
...iota,
|
||||
...dltConnector,
|
||||
}
|
||||
|
||||
66
dlt-connector/src/controller/Community.test.ts
Normal file
66
dlt-connector/src/controller/Community.test.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import 'reflect-metadata'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { create as createCommunity, getAllTopics, isExist } from './Community'
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
import { Community } from '@entity/Community'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
|
||||
jest.mock('@typeorm/DataSource', () => ({
|
||||
getDataSource: () => TestDB.instance.dbConnect,
|
||||
}))
|
||||
|
||||
describe('controller/Community', () => {
|
||||
beforeAll(async () => {
|
||||
await TestDB.instance.setupTestDB()
|
||||
// apolloTestServer = await createApolloTestServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await TestDB.instance.teardownTestDB()
|
||||
})
|
||||
|
||||
describe('createCommunity', () => {
|
||||
it('valid community', async () => {
|
||||
const communityDraft = new CommunityDraft()
|
||||
communityDraft.foreign = false
|
||||
communityDraft.createdAt = '2022-05-01T17:00:12.128Z'
|
||||
communityDraft.uuid = '3d813cbb-47fb-32ba-91df-831e1593ac29'
|
||||
|
||||
const iotaTopic = iotaTopicFromCommunityUUID(communityDraft.uuid)
|
||||
expect(iotaTopic).toEqual('204ef6aed15fbf0f9da5819e88f8eea8e3adbe1e2c2d43280780a4b8c2d32b56')
|
||||
|
||||
const createdAtDate = new Date(communityDraft.createdAt)
|
||||
const communityEntity = createCommunity(communityDraft)
|
||||
expect(communityEntity).toMatchObject({
|
||||
iotaTopic,
|
||||
createdAt: createdAtDate,
|
||||
foreign: false,
|
||||
})
|
||||
await getDataSource().manager.save(communityEntity)
|
||||
})
|
||||
})
|
||||
|
||||
describe('list communities', () => {
|
||||
it('get all topics', async () => {
|
||||
expect(await getAllTopics()).toMatchObject([
|
||||
'204ef6aed15fbf0f9da5819e88f8eea8e3adbe1e2c2d43280780a4b8c2d32b56',
|
||||
])
|
||||
})
|
||||
|
||||
it('isExist with communityDraft', async () => {
|
||||
const communityDraft = new CommunityDraft()
|
||||
communityDraft.foreign = false
|
||||
communityDraft.createdAt = '2022-05-01T17:00:12.128Z'
|
||||
communityDraft.uuid = '3d813cbb-47fb-32ba-91df-831e1593ac29'
|
||||
expect(await isExist(communityDraft)).toBe(true)
|
||||
})
|
||||
|
||||
it('createdAt with ms precision', async () => {
|
||||
const list = await Community.findOne({ where: { foreign: false } })
|
||||
expect(list).toMatchObject({
|
||||
createdAt: new Date('2022-05-01T17:00:12.128Z'),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
28
dlt-connector/src/controller/Community.ts
Normal file
28
dlt-connector/src/controller/Community.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
import { Community } from '@entity/Community'
|
||||
|
||||
export const isExist = async (community: CommunityDraft | string): Promise<boolean> => {
|
||||
const iotaTopic =
|
||||
community instanceof CommunityDraft ? iotaTopicFromCommunityUUID(community.uuid) : community
|
||||
const result = await Community.find({
|
||||
where: { iotaTopic },
|
||||
})
|
||||
return result.length > 0
|
||||
}
|
||||
|
||||
export const create = (community: CommunityDraft, topic?: string): Community => {
|
||||
const communityEntity = Community.create()
|
||||
communityEntity.iotaTopic = topic ?? iotaTopicFromCommunityUUID(community.uuid)
|
||||
communityEntity.createdAt = new Date(community.createdAt)
|
||||
communityEntity.foreign = community.foreign
|
||||
if (!community.foreign) {
|
||||
// TODO: generate keys
|
||||
}
|
||||
return communityEntity
|
||||
}
|
||||
|
||||
export const getAllTopics = async (): Promise<string[]> => {
|
||||
const communities = await Community.find({ select: { iotaTopic: true } })
|
||||
return communities.map((community) => community.iotaTopic)
|
||||
}
|
||||
@ -3,6 +3,8 @@ import { registerEnumType } from 'type-graphql'
|
||||
export enum TransactionErrorType {
|
||||
NOT_IMPLEMENTED_YET = 'Not Implemented yet',
|
||||
MISSING_PARAMETER = 'Missing parameter',
|
||||
ALREADY_EXIST = 'Already exist',
|
||||
DB_ERROR = 'DB Error',
|
||||
}
|
||||
|
||||
registerEnumType(TransactionErrorType, {
|
||||
|
||||
20
dlt-connector/src/graphql/input/CommunityDraft.ts
Normal file
20
dlt-connector/src/graphql/input/CommunityDraft.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
|
||||
import { IsBoolean, IsUUID } from 'class-validator'
|
||||
import { Field, InputType } from 'type-graphql'
|
||||
import { isValidDateString } from '@validator/DateString'
|
||||
|
||||
@InputType()
|
||||
export class CommunityDraft {
|
||||
@Field(() => String)
|
||||
@IsUUID('4')
|
||||
uuid: string
|
||||
|
||||
@Field(() => Boolean)
|
||||
@IsBoolean()
|
||||
foreign: boolean
|
||||
|
||||
@Field(() => String)
|
||||
@isValidDateString()
|
||||
createdAt: string
|
||||
}
|
||||
27
dlt-connector/src/graphql/input/TransactionInput.ts
Executable file
27
dlt-connector/src/graphql/input/TransactionInput.ts
Executable file
@ -0,0 +1,27 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { TransactionType } from '../enum/TransactionType'
|
||||
import { InputType, Field } from 'type-graphql'
|
||||
import { IsEnum, IsInt, Min } from 'class-validator'
|
||||
import { IsPositiveDecimal } from '../validator/Decimal'
|
||||
|
||||
@InputType()
|
||||
export class TransactionInput {
|
||||
@Field(() => TransactionType)
|
||||
@IsEnum(TransactionType)
|
||||
type: TransactionType
|
||||
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => Number)
|
||||
@IsInt()
|
||||
@Min(978346800)
|
||||
createdAt: number
|
||||
|
||||
// @protoField.d(4, 'string')
|
||||
// @Field(() => Decimal)
|
||||
// communitySum: Decimal
|
||||
}
|
||||
@ -3,9 +3,11 @@ import { TransactionError } from './TransactionError'
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionResult {
|
||||
constructor(content: TransactionError | string) {
|
||||
constructor(content?: TransactionError | string) {
|
||||
this.succeed = true
|
||||
if (content instanceof TransactionError) {
|
||||
this.error = content
|
||||
this.succeed = false
|
||||
} else if (typeof content === 'string') {
|
||||
this.messageId = content
|
||||
}
|
||||
@ -18,4 +20,7 @@ export class TransactionResult {
|
||||
// if no error happend, the message id of the iota transaction
|
||||
@Field(() => String, { nullable: true })
|
||||
messageId?: string
|
||||
|
||||
@Field(() => Boolean)
|
||||
succeed: boolean
|
||||
}
|
||||
|
||||
80
dlt-connector/src/graphql/resolver/CommunityResolver.test.ts
Normal file
80
dlt-connector/src/graphql/resolver/CommunityResolver.test.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import 'reflect-metadata'
|
||||
import { ApolloServer } from '@apollo/server'
|
||||
import { createApolloTestServer } from '@test/ApolloServerMock'
|
||||
import assert from 'assert'
|
||||
import { TestDB } from '@test/TestDB'
|
||||
import { TransactionResult } from '../model/TransactionResult'
|
||||
|
||||
let apolloTestServer: ApolloServer
|
||||
|
||||
jest.mock('@typeorm/DataSource', () => ({
|
||||
getDataSource: () => TestDB.instance.dbConnect,
|
||||
}))
|
||||
|
||||
describe('graphql/resolver/CommunityResolver', () => {
|
||||
beforeAll(async () => {
|
||||
apolloTestServer = await createApolloTestServer()
|
||||
})
|
||||
|
||||
describe('tests with db', () => {
|
||||
beforeAll(async () => {
|
||||
await TestDB.instance.setupTestDB()
|
||||
// apolloTestServer = await createApolloTestServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await TestDB.instance.teardownTestDB()
|
||||
})
|
||||
|
||||
it('test add foreign community', async () => {
|
||||
const response = await apolloTestServer.executeOperation({
|
||||
query: 'mutation ($input: CommunityDraft!) { addCommunity(data: $input) {succeed} }',
|
||||
variables: {
|
||||
input: {
|
||||
uuid: '3d813cbb-37fb-42ba-91df-831e1593ac29',
|
||||
foreign: true,
|
||||
createdAt: '2012-04-17T17:12:00.0012Z',
|
||||
},
|
||||
},
|
||||
})
|
||||
assert(response.body.kind === 'single')
|
||||
expect(response.body.singleResult.errors).toBeUndefined()
|
||||
const transactionResult = response.body.singleResult.data?.addCommunity as TransactionResult
|
||||
expect(transactionResult.succeed).toEqual(true)
|
||||
})
|
||||
|
||||
it('test add home community', async () => {
|
||||
const response = await apolloTestServer.executeOperation({
|
||||
query: 'mutation ($input: CommunityDraft!) { addCommunity(data: $input) {succeed} }',
|
||||
variables: {
|
||||
input: {
|
||||
uuid: '3d823cad-37fb-41cd-91df-152e1593ac29',
|
||||
foreign: false,
|
||||
createdAt: '2012-05-12T13:12:00.2917Z',
|
||||
},
|
||||
},
|
||||
})
|
||||
assert(response.body.kind === 'single')
|
||||
expect(response.body.singleResult.errors).toBeUndefined()
|
||||
const transactionResult = response.body.singleResult.data?.addCommunity as TransactionResult
|
||||
expect(transactionResult.succeed).toEqual(true)
|
||||
})
|
||||
|
||||
it('test add existing community', async () => {
|
||||
const response = await apolloTestServer.executeOperation({
|
||||
query: 'mutation ($input: CommunityDraft!) { addCommunity(data: $input) {succeed} }',
|
||||
variables: {
|
||||
input: {
|
||||
uuid: '3d823cad-37fb-41cd-91df-152e1593ac29',
|
||||
foreign: false,
|
||||
createdAt: '2012-05-12T13:12:00.1271Z',
|
||||
},
|
||||
},
|
||||
})
|
||||
assert(response.body.kind === 'single')
|
||||
expect(response.body.singleResult.errors).toBeUndefined()
|
||||
const transactionResult = response.body.singleResult.data?.addCommunity as TransactionResult
|
||||
expect(transactionResult.succeed).toEqual(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
53
dlt-connector/src/graphql/resolver/CommunityResolver.ts
Normal file
53
dlt-connector/src/graphql/resolver/CommunityResolver.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Resolver, Arg, Mutation } from 'type-graphql'
|
||||
|
||||
import { CommunityDraft } from '@input/CommunityDraft'
|
||||
|
||||
import { TransactionResult } from '../model/TransactionResult'
|
||||
import { TransactionError } from '../model/TransactionError'
|
||||
import { create as createCommunity, isExist } from '@/controller/Community'
|
||||
import { TransactionErrorType } from '../enum/TransactionErrorType'
|
||||
import { logger } from '@/server/logger'
|
||||
import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter'
|
||||
|
||||
@Resolver()
|
||||
export class CommunityResolver {
|
||||
@Mutation(() => TransactionResult)
|
||||
async addCommunity(
|
||||
@Arg('data')
|
||||
communityDraft: CommunityDraft,
|
||||
): Promise<TransactionResult> {
|
||||
try {
|
||||
const topic = iotaTopicFromCommunityUUID(communityDraft.uuid)
|
||||
|
||||
// check if community was already written to db
|
||||
if (await isExist(topic)) {
|
||||
return new TransactionResult(
|
||||
new TransactionError(TransactionErrorType.ALREADY_EXIST, 'community already exist!'),
|
||||
)
|
||||
}
|
||||
const community = createCommunity(communityDraft, topic)
|
||||
|
||||
let result: TransactionResult
|
||||
|
||||
if (!communityDraft.foreign) {
|
||||
// TODO: CommunityRoot Transaction for blockchain
|
||||
}
|
||||
try {
|
||||
await community.save()
|
||||
result = new TransactionResult()
|
||||
} catch (err) {
|
||||
logger.error('error saving new community into db: %s', err)
|
||||
result = new TransactionResult(
|
||||
new TransactionError(TransactionErrorType.DB_ERROR, 'error saving community into db'),
|
||||
)
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
if (error instanceof TransactionError) {
|
||||
return new TransactionResult(error)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,10 +4,11 @@ import { buildSchema } from 'type-graphql'
|
||||
|
||||
import { DecimalScalar } from './scalar/Decimal'
|
||||
import { TransactionResolver } from './resolver/TransactionsResolver'
|
||||
import { CommunityResolver } from './resolver/CommunityResolver'
|
||||
|
||||
export const schema = async (): Promise<GraphQLSchema> => {
|
||||
return buildSchema({
|
||||
resolvers: [TransactionResolver],
|
||||
resolvers: [TransactionResolver, CommunityResolver],
|
||||
scalarsMap: [{ type: Decimal, scalar: DecimalScalar }],
|
||||
validate: {
|
||||
validationError: { target: false },
|
||||
|
||||
@ -10,7 +10,7 @@ export function isValidDateString(validationOptions?: ValidationOptions) {
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: string): boolean {
|
||||
return new Date(value).toString() !== 'Invalid Date'
|
||||
return !isNaN(Date.parse(value))
|
||||
},
|
||||
defaultMessage(): string {
|
||||
return `${propertyName} must be a valid date string`
|
||||
|
||||
28
dlt-connector/src/typeorm/DBVersion.ts
Normal file
28
dlt-connector/src/typeorm/DBVersion.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Migration } from '@entity/Migration'
|
||||
|
||||
import { logger } from '@/server/logger'
|
||||
|
||||
const getDBVersion = async (): Promise<string | null> => {
|
||||
try {
|
||||
const [dbVersion] = await Migration.find({ order: { version: 'DESC' }, take: 1 })
|
||||
return dbVersion ? dbVersion.fileName : null
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const checkDBVersion = async (DB_VERSION: string): Promise<boolean> => {
|
||||
const dbVersion = await getDBVersion()
|
||||
if (!dbVersion?.includes(DB_VERSION)) {
|
||||
logger.error(
|
||||
`Wrong database version detected - the backend requires '${DB_VERSION}' but found '${
|
||||
dbVersion ?? 'None'
|
||||
}`,
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export { checkDBVersion, getDBVersion }
|
||||
26
dlt-connector/src/typeorm/DataSource.ts
Normal file
26
dlt-connector/src/typeorm/DataSource.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// TODO This is super weird - since the entities are defined in another project they have their own globals.
|
||||
// We cannot use our connection here, but must use the external typeorm installation
|
||||
import { DataSource as DBDataSource, FileLogger } from '@dbTools/typeorm'
|
||||
import { entities } from '@entity/index'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
const DataSource = new DBDataSource({
|
||||
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,
|
||||
logging: true,
|
||||
logger: new FileLogger('all', {
|
||||
logPath: CONFIG.TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
}),
|
||||
extra: {
|
||||
charset: 'utf8mb4_unicode_ci',
|
||||
},
|
||||
})
|
||||
|
||||
export const getDataSource = () => DataSource
|
||||
17
dlt-connector/src/utils/typeConverter.ts
Normal file
17
dlt-connector/src/utils/typeConverter.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { crypto_generichash as cryptoHash } from 'sodium-native'
|
||||
|
||||
export const uuid4ToBuffer = (uuid: string): Buffer => {
|
||||
// Remove dashes from the UUIDv4 string
|
||||
const cleanedUUID = uuid.replace(/-/g, '')
|
||||
|
||||
// Create a Buffer object from the hexadecimal values
|
||||
const buffer = Buffer.from(cleanedUUID, 'hex')
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
export const iotaTopicFromCommunityUUID = (communityUUID: string): string => {
|
||||
const hash = Buffer.alloc(32)
|
||||
cryptoHash(hash, uuid4ToBuffer(communityUUID))
|
||||
return hash.toString('hex')
|
||||
}
|
||||
77
dlt-connector/test/TestDB.ts
Normal file
77
dlt-connector/test/TestDB.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { DataSource, FileLogger } from '@dbTools/typeorm'
|
||||
import { createDatabase } from 'typeorm-extension'
|
||||
|
||||
import { entities } from '@entity/index'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
export class TestDB {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
private static _instance: TestDB
|
||||
|
||||
private constructor() {
|
||||
if (!CONFIG.DB_DATABASE_TEST) {
|
||||
throw new LogError('no test db in config')
|
||||
}
|
||||
if (CONFIG.DB_DATABASE === CONFIG.DB_DATABASE_TEST) {
|
||||
throw new LogError(
|
||||
'main db is the same as test db, not good because test db will be cleared after each test run',
|
||||
)
|
||||
}
|
||||
this.dbConnect = new DataSource({
|
||||
type: 'mysql',
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE_TEST,
|
||||
entities,
|
||||
synchronize: true,
|
||||
dropSchema: true,
|
||||
logging: true,
|
||||
logger: new FileLogger('all', {
|
||||
logPath: CONFIG.TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
}),
|
||||
extra: {
|
||||
charset: 'utf8mb4_unicode_ci',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public static get instance(): TestDB {
|
||||
if (!this._instance) this._instance = new TestDB()
|
||||
return this._instance
|
||||
}
|
||||
|
||||
public dbConnect!: DataSource
|
||||
|
||||
async setupTestDB() {
|
||||
// eslint-disable-next-line no-console
|
||||
try {
|
||||
if (!CONFIG.DB_DATABASE_TEST) {
|
||||
throw new LogError('no test db in config')
|
||||
}
|
||||
await createDatabase({
|
||||
ifNotExist: true,
|
||||
options: {
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4_unicode_ci',
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE_TEST,
|
||||
},
|
||||
})
|
||||
await this.dbConnect.initialize()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
async teardownTestDB() {
|
||||
await this.dbConnect.destroy()
|
||||
}
|
||||
}
|
||||
@ -57,8 +57,11 @@
|
||||
"@test/*": ["test/*"],
|
||||
"@proto/*" : ["src/proto/*"],
|
||||
"@controller/*": ["src/controller/*"],
|
||||
"@validator/*" : ["src/graphql/validator/*"]
|
||||
"@validator/*" : ["src/graphql/validator/*"],
|
||||
"@typeorm/*" : ["src/typeorm/*"],
|
||||
/* external */
|
||||
"@dbTools/*": ["../dlt-database/src/*", "../../dlt-database/build/src/*"],
|
||||
"@entity/*": ["../dlt-database/entity/*", "../../dlt-database/build/entity/*"]
|
||||
},
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */
|
||||
@ -82,4 +85,11 @@
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../dlt-database/tsconfig.json",
|
||||
// add 'prepend' if you want to include the referenced project in your output file
|
||||
// "prepend": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,28 +2,26 @@ import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { User } from './User'
|
||||
import { Community } from './Community'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
import { ConfirmedTransaction } from './ConfirmedTransaction'
|
||||
import { User } from '../User'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
import { ConfirmedTransaction } from '../ConfirmedTransaction'
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { AccountCommunity } from '../AccountCommunity'
|
||||
|
||||
@Entity('accounts')
|
||||
export class Account {
|
||||
export class Account extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ManyToOne(() => User, (user) => user.accounts) // Assuming you have a User entity with 'accounts' relation
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User
|
||||
user?: User
|
||||
|
||||
// if user id is null, account belongs to community gmw or auf
|
||||
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: true })
|
||||
@ -38,10 +36,15 @@ export class Account {
|
||||
@Column({ type: 'tinyint', unsigned: true })
|
||||
type: number
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', precision: 3, nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@Column({
|
||||
@ -56,17 +59,14 @@ export class Account {
|
||||
@Column({
|
||||
name: 'balance_date',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
balanceDate: Date
|
||||
|
||||
@ManyToMany(() => Community, (community) => community.communityAccounts)
|
||||
@JoinTable({
|
||||
name: 'accounts_communities',
|
||||
joinColumn: { name: 'account_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'community_id', referencedColumnName: 'id' },
|
||||
})
|
||||
accountCommunities: Community[]
|
||||
@OneToMany(() => AccountCommunity, (accountCommunity) => accountCommunity.account)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
accountCommunities: AccountCommunity[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.signingAccount)
|
||||
transactionRecipesSigning?: TransactionRecipe[]
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, BaseEntity } from 'typeorm'
|
||||
|
||||
import { Account } from './Account'
|
||||
import { Community } from './Community'
|
||||
import { Account } from '../Account'
|
||||
import { Community } from '../Community'
|
||||
|
||||
@Entity('accounts_communities')
|
||||
export class AccountCommunity {
|
||||
export class AccountCommunity extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ -15,16 +15,16 @@ export class AccountCommunity {
|
||||
@Column({ name: 'account_id', type: 'int', unsigned: true })
|
||||
accountId: number
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.communityAccounts)
|
||||
@ManyToOne(() => Community, (community) => community.accountCommunities)
|
||||
@JoinColumn({ name: 'community_id' })
|
||||
community: Community
|
||||
|
||||
@Column({ name: 'community_id', type: 'int', unsigned: true })
|
||||
communityId: number
|
||||
|
||||
@Column({ name: 'valid_from', type: 'datetime' })
|
||||
@Column({ name: 'valid_from', type: 'datetime', precision: 3 })
|
||||
validFrom: Date
|
||||
|
||||
@Column({ name: 'valid_to', type: 'datetime', nullable: true })
|
||||
@Column({ name: 'valid_to', type: 'datetime', precision: 3, nullable: true })
|
||||
validTo?: Date
|
||||
}
|
||||
|
||||
@ -2,18 +2,17 @@ import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
OneToMany,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { Account } from './Account'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
import { Account } from '../Account'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
import { AccountCommunity } from '../AccountCommunity'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community {
|
||||
export class Community extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ -46,19 +45,20 @@ export class Community {
|
||||
@JoinColumn({ name: 'auf_account_id' })
|
||||
aufAccount?: Account
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', precision: 3, nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@ManyToMany(() => Account, (account) => account.accountCommunities)
|
||||
@JoinTable({
|
||||
name: 'accounts_communities',
|
||||
joinColumn: { name: 'community_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'account_id', referencedColumnName: 'id' },
|
||||
})
|
||||
communityAccounts: Account[]
|
||||
@OneToMany(() => AccountCommunity, (accountCommunity) => accountCommunity.community)
|
||||
@JoinColumn({ name: 'community_id' })
|
||||
accountCommunities: AccountCommunity[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.senderCommunity)
|
||||
transactionRecipesSender?: TransactionRecipe[]
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToOne } from 'typeorm'
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Account } from './Account'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
import { Account } from '../Account'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
|
||||
@Entity('confirmed_transactions')
|
||||
export class ConfirmedTransaction {
|
||||
export class ConfirmedTransaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@ -44,6 +52,6 @@ export class ConfirmedTransaction {
|
||||
@Column({ name: 'iota_milestone', type: 'bigint' })
|
||||
iotaMilestone: number
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime' })
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', precision: 3 })
|
||||
confirmedAt: Date
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm'
|
||||
|
||||
@Entity('invalid_transactions')
|
||||
export class InvalidTransaction {
|
||||
export class InvalidTransaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
|
||||
13
dlt-database/entity/0001-init_db/Migration.ts
Normal file
13
dlt-database/entity/0001-init_db/Migration.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
@Entity('migrations')
|
||||
export class Migration extends BaseEntity {
|
||||
@PrimaryGeneratedColumn() // This is actually not a primary column
|
||||
version: number
|
||||
|
||||
@Column({ length: 256, nullable: true, default: null })
|
||||
fileName: string
|
||||
|
||||
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
|
||||
date: Date
|
||||
}
|
||||
@ -2,20 +2,20 @@ import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Account } from './Account'
|
||||
import { Community } from './Community'
|
||||
import { ConfirmedTransaction } from './ConfirmedTransaction'
|
||||
import { Account } from '../Account'
|
||||
import { Community } from '../Community'
|
||||
import { ConfirmedTransaction } from '../ConfirmedTransaction'
|
||||
|
||||
@Entity('transaction_recipes')
|
||||
export class TransactionRecipe {
|
||||
export class TransactionRecipe extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@ -48,7 +48,7 @@ export class TransactionRecipe {
|
||||
@JoinColumn({ name: 'recipient_community_id' })
|
||||
recipientCommunity?: Community
|
||||
|
||||
@Column({ name: 'sender_community_id', type: 'int', unsigned: true, nullable: true })
|
||||
@Column({ name: 'recipient_community_id', type: 'int', unsigned: true, nullable: true })
|
||||
recipientCommunityId?: number
|
||||
|
||||
@Column({
|
||||
@ -63,7 +63,7 @@ export class TransactionRecipe {
|
||||
@Column({ type: 'tinyint' })
|
||||
type: number
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime' })
|
||||
@Column({ name: 'created_at', type: 'datetime', precision: 3 })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'body_bytes', type: 'blob' })
|
||||
|
||||
@ -1,14 +1,6 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm'
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, JoinColumn } from 'typeorm'
|
||||
|
||||
import { Account } from './Account'
|
||||
import { Account } from '../Account'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@ -26,9 +18,10 @@ export class User extends BaseEntity {
|
||||
@Column({ name: 'derive1_pubkey', type: 'binary', length: 32, unique: true })
|
||||
derive1Pubkey: Buffer
|
||||
|
||||
@CreateDateColumn({
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
@ -36,6 +29,7 @@ export class User extends BaseEntity {
|
||||
@Column({
|
||||
name: 'confirmed_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
nullable: true,
|
||||
})
|
||||
confirmedAt?: Date
|
||||
|
||||
78
dlt-database/entity/0002-refactor_add_community/Account.ts
Normal file
78
dlt-database/entity/0002-refactor_add_community/Account.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { User } from '../User'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
import { ConfirmedTransaction } from '../ConfirmedTransaction'
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { AccountCommunity } from '../AccountCommunity'
|
||||
|
||||
@Entity('accounts')
|
||||
export class Account extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ManyToOne(() => User, (user) => user.accounts) // Assuming you have a User entity with 'accounts' relation
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user?: User
|
||||
|
||||
// if user id is null, account belongs to community gmw or auf
|
||||
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: true })
|
||||
userId?: number
|
||||
|
||||
@Column({ name: 'derivation_index', type: 'int', unsigned: true })
|
||||
derivationIndex: number
|
||||
|
||||
@Column({ name: 'derive2_pubkey', type: 'binary', length: 32, unique: true })
|
||||
derive2Pubkey: Buffer
|
||||
|
||||
@Column({ type: 'tinyint', unsigned: true })
|
||||
type: number
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
default: 0,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
balance: Decimal
|
||||
|
||||
@Column({
|
||||
name: 'balance_date',
|
||||
type: 'datetime',
|
||||
default: () => 'CURRENT_TIMESTAMP()',
|
||||
})
|
||||
balanceDate: Date
|
||||
|
||||
@OneToMany(() => AccountCommunity, (accountCommunity) => accountCommunity.account)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
accountCommunities: AccountCommunity[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.signingAccount)
|
||||
transactionRecipesSigning?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientAccount)
|
||||
transactionRecipesRecipient?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => ConfirmedTransaction, (transaction) => transaction.account)
|
||||
confirmedTransactions?: ConfirmedTransaction[]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, BaseEntity } from 'typeorm'
|
||||
|
||||
import { Account } from '../Account'
|
||||
import { Community } from '../Community'
|
||||
|
||||
@Entity('accounts_communities')
|
||||
export class AccountCommunity extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.accountCommunities)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
account: Account
|
||||
|
||||
@Column({ name: 'account_id', type: 'int', unsigned: true })
|
||||
accountId: number
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.accountCommunities)
|
||||
@JoinColumn({ name: 'community_id' })
|
||||
community: Community
|
||||
|
||||
@Column({ name: 'community_id', type: 'int', unsigned: true })
|
||||
communityId: number
|
||||
|
||||
@Column({ name: 'valid_from', type: 'datetime' })
|
||||
validFrom: Date
|
||||
|
||||
@Column({ name: 'valid_to', type: 'datetime', nullable: true })
|
||||
validTo?: Date
|
||||
}
|
||||
68
dlt-database/entity/0002-refactor_add_community/Community.ts
Normal file
68
dlt-database/entity/0002-refactor_add_community/Community.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
OneToMany,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { Account } from '../Account'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
import { AccountCommunity } from '../AccountCommunity'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'iota_topic', collation: 'utf8mb4_unicode_ci' })
|
||||
iotaTopic: string
|
||||
|
||||
@Column({ name: 'root_pubkey', type: 'binary', length: 32, unique: true, nullable: true })
|
||||
rootPubkey?: Buffer
|
||||
|
||||
@Column({ name: 'root_privkey', type: 'binary', length: 64, nullable: true })
|
||||
rootPrivkey?: Buffer
|
||||
|
||||
@Column({ name: 'root_chaincode', type: 'binary', length: 32, nullable: true })
|
||||
rootChaincode?: Buffer
|
||||
|
||||
@Column({ type: 'tinyint', default: true })
|
||||
foreign: boolean
|
||||
|
||||
@Column({ name: 'gmw_account_id', type: 'int', unsigned: true, nullable: true })
|
||||
gmwAccountId?: number
|
||||
|
||||
@OneToOne(() => Account)
|
||||
@JoinColumn({ name: 'gmw_account_id' })
|
||||
gmwAccount?: Account
|
||||
|
||||
@Column({ name: 'auf_account_id', type: 'int', unsigned: true, nullable: true })
|
||||
aufAccountId?: number
|
||||
|
||||
@OneToOne(() => Account)
|
||||
@JoinColumn({ name: 'auf_account_id' })
|
||||
aufAccount?: Account
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@OneToMany(() => AccountCommunity, (accountCommunity) => accountCommunity.community)
|
||||
@JoinColumn({ name: 'community_id' })
|
||||
accountCommunities: AccountCommunity[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.senderCommunity)
|
||||
transactionRecipesSender?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientCommunity)
|
||||
transactionRecipesRecipient?: TransactionRecipe[]
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
BaseEntity,
|
||||
} from 'typeorm'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Account } from '../Account'
|
||||
import { TransactionRecipe } from '../TransactionRecipe'
|
||||
|
||||
@Entity('confirmed_transactions')
|
||||
export class ConfirmedTransaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@OneToOne(() => TransactionRecipe, (recipe) => recipe.confirmedTransaction)
|
||||
@JoinColumn({ name: 'transaction_recipe_id' })
|
||||
transactionRecipe: TransactionRecipe
|
||||
|
||||
@Column({ name: 'transaction_recipe_id', type: 'int', unsigned: true })
|
||||
transactionRecipeId: number
|
||||
|
||||
@Column({ type: 'bigint' })
|
||||
nr: number
|
||||
|
||||
@Column({ type: 'binary', length: 48 })
|
||||
runningHash: Buffer
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.confirmedTransactions)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
account: Account
|
||||
|
||||
@Column({ name: 'account_id', type: 'int', unsigned: true })
|
||||
accountId: number
|
||||
|
||||
@Column({
|
||||
name: 'account_balance',
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
nullable: false,
|
||||
default: 0,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
accountBalance: Decimal
|
||||
|
||||
@Column({ name: 'iota_milestone', type: 'bigint' })
|
||||
iotaMilestone: number
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime' })
|
||||
confirmedAt: Date
|
||||
}
|
||||
39
dlt-database/entity/0002-refactor_add_community/User.ts
Normal file
39
dlt-database/entity/0002-refactor_add_community/User.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, JoinColumn } from 'typeorm'
|
||||
|
||||
import { Account } from '../Account'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({
|
||||
name: 'gradido_id',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
gradidoID?: string
|
||||
|
||||
@Column({ name: 'derive1_pubkey', type: 'binary', length: 32, unique: true })
|
||||
derive1Pubkey: Buffer
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 3,
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'confirmed_at',
|
||||
type: 'datetime',
|
||||
nullable: true,
|
||||
})
|
||||
confirmedAt?: Date
|
||||
|
||||
@OneToMany(() => Account, (account) => account.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
accounts?: Account[]
|
||||
}
|
||||
@ -1 +1 @@
|
||||
export { Account } from './0001-init_db/Account'
|
||||
export { Account } from './0002-refactor_add_community/Account'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { AccountCommunity } from './0001-init_db/AccountCommunity'
|
||||
export { AccountCommunity } from './0002-refactor_add_community/AccountCommunity'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { Community } from './0001-init_db/Community'
|
||||
export { Community } from './0002-refactor_add_community/Community'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { ConfirmedTransaction } from './0001-init_db/ConfirmedTransaction'
|
||||
export { ConfirmedTransaction } from './0002-refactor_add_community/ConfirmedTransaction'
|
||||
|
||||
1
dlt-database/entity/Migration.ts
Normal file
1
dlt-database/entity/Migration.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Migration } from './0001-init_db/Migration'
|
||||
@ -1 +1 @@
|
||||
export { User } from './0001-init_db/User'
|
||||
export { User } from './0002-refactor_add_community/User'
|
||||
|
||||
19
dlt-database/entity/index.ts
Normal file
19
dlt-database/entity/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Account } from './Account'
|
||||
import { AccountCommunity } from './AccountCommunity'
|
||||
import { Community } from './Community'
|
||||
import { ConfirmedTransaction } from './ConfirmedTransaction'
|
||||
import { InvalidTransaction } from './InvalidTransaction'
|
||||
import { Migration } from './Migration'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
import { User } from './User'
|
||||
|
||||
export const entities = [
|
||||
AccountCommunity,
|
||||
Account,
|
||||
Community,
|
||||
ConfirmedTransaction,
|
||||
InvalidTransaction,
|
||||
Migration,
|
||||
TransactionRecipe,
|
||||
User,
|
||||
]
|
||||
@ -125,6 +125,6 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom
|
||||
await queryFn(`DROP TABLE IF EXISTS \`accounts_communities\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`transaction_recipes\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`confirmed_transactions\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`community\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`communities\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`invalid_transactions\`;`)
|
||||
}
|
||||
|
||||
61
dlt-database/migrations/0002-refactor_add_community.ts
Normal file
61
dlt-database/migrations/0002-refactor_add_community.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
// write upgrade logic as parameter of queryFn
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`root_privkey\` binary(64) NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`root_pubkey\` binary(32) NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`root_chaincode\` binary(32) NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`confirmed_at\` datetime NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(`ALTER TABLE \`users\` MODIFY COLUMN \`confirmed_at\` datetime NULL DEFAULT NULL;`)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts\` MODIFY COLUMN \`confirmed_at\` datetime NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts\` MODIFY COLUMN \`balance_date\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts_communities\` MODIFY COLUMN \`valid_from\` datetime NOT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts_communities\` MODIFY COLUMN \`valid_to\` datetime NULL DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`confirmed_transactions\` MODIFY COLUMN \`confirmed_at\` datetime NOT NULL;`,
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`root_privkey\` binary(32) DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(`ALTER TABLE \`communities\` MODIFY COLUMN \`root_pubkey\` binary(32) NOT NULL;`)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`root_chaincode\` binary(32) DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`communities\` MODIFY COLUMN \`confirmed_at\` datetime(3) DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(`ALTER TABLE \`users\` MODIFY COLUMN \`confirmed_at\` datetime(3) DEFAULT NULL;`)
|
||||
await queryFn(`ALTER TABLE \`accounts\` MODIFY COLUMN \`confirmed_at\` datetime(3) DEFAULT NULL;`)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts\` MODIFY COLUMN \`balance_date\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts_communities\` MODIFY COLUMN \`valid_from\` datetime(3) NOT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`accounts_communities\` MODIFY COLUMN \`valid_to\` datetime(3) DEFAULT NULL;`,
|
||||
)
|
||||
await queryFn(
|
||||
`ALTER TABLE \`confirmed_transactions\` MODIFY COLUMN \`confirmed_at\` datetime(3) NOT NULL;`,
|
||||
)
|
||||
}
|
||||
@ -103,6 +103,7 @@ services:
|
||||
- dlt_connector_modules:/app/node_modules
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./dlt-connector:/app
|
||||
- ./dlt-database:/dlt-database
|
||||
|
||||
########################################################
|
||||
# FEDERATION ###########################################
|
||||
|
||||
@ -64,6 +64,7 @@ services:
|
||||
- internal-net
|
||||
environment:
|
||||
- NODE_ENV="test"
|
||||
- DB_HOST=mariadb
|
||||
|
||||
########################################################
|
||||
# DATABASE #############################################
|
||||
|
||||
@ -173,6 +173,7 @@ services:
|
||||
- BUILD_VERSION
|
||||
- BUILD_COMMIT
|
||||
- NODE_ENV="production"
|
||||
- DB_HOST=mariadb
|
||||
# Application only envs
|
||||
volumes:
|
||||
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user