mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #1979 from gradido/crud-contribution-link
feat: CRUD for Contribution Links in Admin Resolver
This commit is contained in:
commit
a6ca269352
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -528,7 +528,7 @@ jobs:
|
|||||||
report_name: Coverage Backend
|
report_name: Coverage Backend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./backend/coverage/lcov.info
|
result_path: ./backend/coverage/lcov.info
|
||||||
min_coverage: 68
|
min_coverage: 70
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
CONFIG_VERSION=v6.2022-04-21
|
CONFIG_VERSION=v7.2022-06-15
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
PORT=4000
|
PORT=4000
|
||||||
@ -28,6 +28,7 @@ COMMUNITY_NAME=Gradido Entwicklung
|
|||||||
COMMUNITY_URL=http://localhost/
|
COMMUNITY_URL=http://localhost/
|
||||||
COMMUNITY_REGISTER_URL=http://localhost/register
|
COMMUNITY_REGISTER_URL=http://localhost/register
|
||||||
COMMUNITY_REDEEM_URL=http://localhost/redeem/{code}
|
COMMUNITY_REDEEM_URL=http://localhost/redeem/{code}
|
||||||
|
COMMUNITY_REDEEM_CONTRIBUTION_URL=http://localhost/redeem/CL-{code}
|
||||||
COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido.
|
COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido.
|
||||||
|
|
||||||
# Login Server
|
# Login Server
|
||||||
|
|||||||
@ -27,6 +27,7 @@ COMMUNITY_NAME=$COMMUNITY_NAME
|
|||||||
COMMUNITY_URL=$COMMUNITY_URL
|
COMMUNITY_URL=$COMMUNITY_URL
|
||||||
COMMUNITY_REGISTER_URL=$COMMUNITY_REGISTER_URL
|
COMMUNITY_REGISTER_URL=$COMMUNITY_REGISTER_URL
|
||||||
COMMUNITY_REDEEM_URL=$COMMUNITY_REDEEM_URL
|
COMMUNITY_REDEEM_URL=$COMMUNITY_REDEEM_URL
|
||||||
|
COMMUNITY_REDEEM_CONTRIBUTION_URL=$COMMUNITY_REDEEM_CONTRIBUTION_URL
|
||||||
COMMUNITY_DESCRIPTION=$COMMUNITY_DESCRIPTION
|
COMMUNITY_DESCRIPTION=$COMMUNITY_DESCRIPTION
|
||||||
|
|
||||||
# Login Server
|
# Login Server
|
||||||
|
|||||||
@ -37,4 +37,8 @@ export enum RIGHTS {
|
|||||||
UNDELETE_USER = 'UNDELETE_USER',
|
UNDELETE_USER = 'UNDELETE_USER',
|
||||||
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
|
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
|
||||||
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
|
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
|
||||||
|
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
||||||
|
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
||||||
|
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
||||||
|
UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const constants = {
|
|||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
CONFIG_VERSION: {
|
CONFIG_VERSION: {
|
||||||
DEFAULT: 'DEFAULT',
|
DEFAULT: 'DEFAULT',
|
||||||
EXPECTED: 'v6.2022-04-21',
|
EXPECTED: 'v7.2022-06-15',
|
||||||
CURRENT: '',
|
CURRENT: '',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -54,6 +54,8 @@ const community = {
|
|||||||
COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/',
|
COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/',
|
||||||
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register',
|
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register',
|
||||||
COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}',
|
COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}',
|
||||||
|
COMMUNITY_REDEEM_CONTRIBUTION_URL:
|
||||||
|
process.env.COMMUNITY_REDEEM_CONTRIBUTION_URL || 'http://localhost/redeem/CL-{code}',
|
||||||
COMMUNITY_DESCRIPTION:
|
COMMUNITY_DESCRIPTION:
|
||||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
||||||
}
|
}
|
||||||
|
|||||||
29
backend/src/graphql/arg/ContributionLinkArgs.ts
Normal file
29
backend/src/graphql/arg/ContributionLinkArgs.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ArgsType, Field, Int } from 'type-graphql'
|
||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
|
||||||
|
@ArgsType()
|
||||||
|
export default class ContributionLinkArgs {
|
||||||
|
@Field(() => Decimal)
|
||||||
|
amount: Decimal
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
memo: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
cycle: string
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
validFrom?: string | null
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
validTo?: string | null
|
||||||
|
|
||||||
|
@Field(() => Decimal, { nullable: true })
|
||||||
|
maxAmountPerMonth: Decimal | null
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
maxPerCycle: number
|
||||||
|
}
|
||||||
28
backend/src/graphql/enum/ContributionCycleType.ts
Normal file
28
backend/src/graphql/enum/ContributionCycleType.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { registerEnumType } from 'type-graphql'
|
||||||
|
|
||||||
|
export enum ContributionCycleType {
|
||||||
|
ONCE = 'once',
|
||||||
|
HOUR = 'hour',
|
||||||
|
TWO_HOURS = 'two_hours',
|
||||||
|
FOUR_HOURS = 'four_hours',
|
||||||
|
EIGHT_HOURS = 'eight_hours',
|
||||||
|
HALF_DAY = 'half_day',
|
||||||
|
DAY = 'day',
|
||||||
|
TWO_DAYS = 'two_days',
|
||||||
|
THREE_DAYS = 'three_days',
|
||||||
|
FOUR_DAYS = 'four_days',
|
||||||
|
FIVE_DAYS = 'five_days',
|
||||||
|
SIX_DAYS = 'six_days',
|
||||||
|
WEEK = 'week',
|
||||||
|
TWO_WEEKS = 'two_weeks',
|
||||||
|
MONTH = 'month',
|
||||||
|
TWO_MONTH = 'two_month',
|
||||||
|
QUARTER = 'quarter',
|
||||||
|
HALF_YEAR = 'half_year',
|
||||||
|
YEAR = 'year',
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(ContributionCycleType, {
|
||||||
|
name: 'ContributionCycleType', // this one is mandatory
|
||||||
|
description: 'Name of the Type of the ContributionCycle', // this one is optional
|
||||||
|
})
|
||||||
62
backend/src/graphql/model/ContributionLink.ts
Normal file
62
backend/src/graphql/model/ContributionLink.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { ObjectType, Field, Int } from 'type-graphql'
|
||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { ContributionLink as dbContributionLink } from '@entity/ContributionLink'
|
||||||
|
import CONFIG from '@/config'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class ContributionLink {
|
||||||
|
constructor(contributionLink: dbContributionLink) {
|
||||||
|
this.id = contributionLink.id
|
||||||
|
this.amount = contributionLink.amount
|
||||||
|
this.name = contributionLink.name
|
||||||
|
this.memo = contributionLink.memo
|
||||||
|
this.createdAt = contributionLink.createdAt
|
||||||
|
this.deletedAt = contributionLink.deletedAt
|
||||||
|
this.validFrom = contributionLink.validFrom
|
||||||
|
this.validTo = contributionLink.validTo
|
||||||
|
this.maxAmountPerMonth = contributionLink.maxAmountPerMonth
|
||||||
|
this.cycle = contributionLink.cycle
|
||||||
|
this.maxPerCycle = contributionLink.maxPerCycle
|
||||||
|
this.code = contributionLink.code
|
||||||
|
this.link = CONFIG.COMMUNITY_REDEEM_CONTRIBUTION_URL.replace(/{code}/g, this.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
amount: Decimal
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
memo: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
code: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
link: string
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
deletedAt: Date | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
validFrom: Date | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
validTo: Date | null
|
||||||
|
|
||||||
|
@Field(() => Decimal, { nullable: true })
|
||||||
|
maxAmountPerMonth: Decimal | null
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
cycle: string
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
maxPerCycle: number
|
||||||
|
}
|
||||||
11
backend/src/graphql/model/ContributionLinkList.ts
Normal file
11
backend/src/graphql/model/ContributionLinkList.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ObjectType, Field } from 'type-graphql'
|
||||||
|
import { ContributionLink } from '@model/ContributionLink'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class ContributionLinkList {
|
||||||
|
@Field(() => [ContributionLink])
|
||||||
|
links: ContributionLink[]
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
count: number
|
||||||
|
}
|
||||||
@ -20,12 +20,16 @@ import {
|
|||||||
updatePendingCreation,
|
updatePendingCreation,
|
||||||
deletePendingCreation,
|
deletePendingCreation,
|
||||||
confirmPendingCreation,
|
confirmPendingCreation,
|
||||||
|
createContributionLink,
|
||||||
|
deleteContributionLink,
|
||||||
|
updateContributionLink,
|
||||||
} from '@/seeds/graphql/mutations'
|
} from '@/seeds/graphql/mutations'
|
||||||
import {
|
import {
|
||||||
getPendingCreations,
|
getPendingCreations,
|
||||||
login,
|
login,
|
||||||
searchUsers,
|
searchUsers,
|
||||||
listTransactionLinksAdmin,
|
listTransactionLinksAdmin,
|
||||||
|
listContributionLinks,
|
||||||
} from '@/seeds/graphql/queries'
|
} from '@/seeds/graphql/queries'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
@ -34,6 +38,7 @@ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
|
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||||
|
|
||||||
// mock account activation email to avoid console spam
|
// mock account activation email to avoid console spam
|
||||||
jest.mock('@/mailer/sendAccountActivationEmail', () => {
|
jest.mock('@/mailer/sendAccountActivationEmail', () => {
|
||||||
@ -1593,4 +1598,361 @@ describe('AdminResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Contribution Links', () => {
|
||||||
|
const variables = {
|
||||||
|
amount: new Decimal(200),
|
||||||
|
name: 'Dokumenta 2022',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||||
|
cycle: 'once',
|
||||||
|
validFrom: new Date(2022, 5, 18).toISOString(),
|
||||||
|
validTo: new Date(2022, 7, 14).toISOString(),
|
||||||
|
maxAmountPerMonth: new Decimal(200),
|
||||||
|
maxPerCycle: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
describe('createContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('listContributionLinks', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(query({ query: listContributionLinks })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: updateContributionLink,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
id: -1,
|
||||||
|
amount: new Decimal(400),
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('deleteContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: deleteContributionLink, variables: { id: -1 } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
describe('without admin rights', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
user = await userFactory(testEnv, bibiBloxberg)
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('listContributionLinks', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(query({ query: listContributionLinks })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: updateContributionLink,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
id: -1,
|
||||||
|
amount: new Decimal(400),
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('deleteContributionLink', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: deleteContributionLink, variables: { id: -1 } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with admin rights', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
user = await userFactory(testEnv, peterLustig)
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createContributionLink', () => {
|
||||||
|
it('returns a contribution link object', async () => {
|
||||||
|
await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
createContributionLink: expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
amount: '200',
|
||||||
|
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||||
|
link: expect.stringMatching(/^.*?\/CL-[0-9a-f]{24,24}$/),
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
name: 'Dokumenta 2022',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||||
|
validFrom: expect.any(String),
|
||||||
|
validTo: expect.any(String),
|
||||||
|
maxAmountPerMonth: '200',
|
||||||
|
cycle: 'once',
|
||||||
|
maxPerCycle: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a contribution link stored in db', async () => {
|
||||||
|
const cls = await DbContributionLink.find()
|
||||||
|
expect(cls).toHaveLength(1)
|
||||||
|
expect(cls[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
name: 'Dokumenta 2022',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||||
|
validFrom: new Date('2022-06-18T00:00:00.000Z'),
|
||||||
|
validTo: new Date('2022-08-14T00:00:00.000Z'),
|
||||||
|
cycle: 'once',
|
||||||
|
maxPerCycle: 1,
|
||||||
|
totalMaxCountOfContribution: null,
|
||||||
|
maxAccountBalance: null,
|
||||||
|
minGapHours: null,
|
||||||
|
createdAt: expect.any(Date),
|
||||||
|
deletedAt: null,
|
||||||
|
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||||
|
linkEnabled: true,
|
||||||
|
// amount: '200',
|
||||||
|
// maxAmountPerMonth: '200',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('listContributionLinks', () => {
|
||||||
|
describe('one link in DB', () => {
|
||||||
|
it('returns the link and count 1', async () => {
|
||||||
|
await expect(query({ query: listContributionLinks })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listContributionLinks: {
|
||||||
|
links: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: '200',
|
||||||
|
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||||
|
link: expect.stringMatching(/^.*?\/CL-[0-9a-f]{24,24}$/),
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
name: 'Dokumenta 2022',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||||
|
validFrom: expect.any(String),
|
||||||
|
validTo: expect.any(String),
|
||||||
|
maxAmountPerMonth: '200',
|
||||||
|
cycle: 'once',
|
||||||
|
maxPerCycle: 1,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateContributionLink', () => {
|
||||||
|
describe('no valid id', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: updateContributionLink,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
id: -1,
|
||||||
|
amount: new Decimal(400),
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('Contribution Link not found to given id.')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('valid id', () => {
|
||||||
|
let linkId: number
|
||||||
|
beforeAll(async () => {
|
||||||
|
const links = await query({ query: listContributionLinks })
|
||||||
|
linkId = links.data.listContributionLinks.links[0].id
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns updated contribution link object', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: updateContributionLink,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
id: linkId,
|
||||||
|
amount: new Decimal(400),
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
updateContributionLink: {
|
||||||
|
id: linkId,
|
||||||
|
amount: '400',
|
||||||
|
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||||
|
link: expect.stringMatching(/^.*?\/CL-[0-9a-f]{24,24}$/),
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
validFrom: expect.any(String),
|
||||||
|
validTo: expect.any(String),
|
||||||
|
maxAmountPerMonth: '200',
|
||||||
|
cycle: 'once',
|
||||||
|
maxPerCycle: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updated the DB record', async () => {
|
||||||
|
await expect(DbContributionLink.findOne(linkId)).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: linkId,
|
||||||
|
name: 'Dokumenta 2023',
|
||||||
|
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||||
|
// amount: '400',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('deleteContributionLink', () => {
|
||||||
|
describe('no valid id', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: deleteContributionLink, variables: { id: -1 } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('Contribution Link not found to given id.')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('valid id', () => {
|
||||||
|
let linkId: number
|
||||||
|
beforeAll(async () => {
|
||||||
|
const links = await query({ query: listContributionLinks })
|
||||||
|
linkId = links.data.listContributionLinks.links[0].id
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns a date string', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: deleteContributionLink, variables: { id: linkId } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
deleteContributionLink: expect.any(String),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not list this contribution link anymore', async () => {
|
||||||
|
await expect(query({ query: listContributionLinks })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listContributionLinks: {
|
||||||
|
links: [],
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Context, getUser } from '@/server/context'
|
import { Context, getUser } from '@/server/context'
|
||||||
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx, Int } from 'type-graphql'
|
import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx, Int } from 'type-graphql'
|
||||||
import {
|
import {
|
||||||
getCustomRepository,
|
getCustomRepository,
|
||||||
@ -14,12 +15,16 @@ import { UserAdmin, SearchUsersResult } from '@model/UserAdmin'
|
|||||||
import { PendingCreation } from '@model/PendingCreation'
|
import { PendingCreation } from '@model/PendingCreation'
|
||||||
import { CreatePendingCreations } from '@model/CreatePendingCreations'
|
import { CreatePendingCreations } from '@model/CreatePendingCreations'
|
||||||
import { UpdatePendingCreation } from '@model/UpdatePendingCreation'
|
import { UpdatePendingCreation } from '@model/UpdatePendingCreation'
|
||||||
|
import { ContributionLink } from '@model/ContributionLink'
|
||||||
|
import { ContributionLinkList } from '@model/ContributionLinkList'
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { UserRepository } from '@repository/User'
|
import { UserRepository } from '@repository/User'
|
||||||
import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs'
|
import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs'
|
||||||
import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs'
|
import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs'
|
||||||
import SearchUsersArgs from '@arg/SearchUsersArgs'
|
import SearchUsersArgs from '@arg/SearchUsersArgs'
|
||||||
|
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
|
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||||
import { Transaction } from '@model/Transaction'
|
import { Transaction } from '@model/Transaction'
|
||||||
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
||||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||||
@ -39,6 +44,7 @@ import { Order } from '@enum/Order'
|
|||||||
import { communityUser } from '@/util/communityUser'
|
import { communityUser } from '@/util/communityUser'
|
||||||
import { checkOptInCode, activationLink, printTimeDuration } from './UserResolver'
|
import { checkOptInCode, activationLink, printTimeDuration } from './UserResolver'
|
||||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
||||||
|
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
|
|
||||||
// const EMAIL_OPT_IN_REGISTER = 1
|
// const EMAIL_OPT_IN_REGISTER = 1
|
||||||
@ -460,6 +466,99 @@ export class AdminResolver {
|
|||||||
linkList: transactionLinks.map((tl) => new TransactionLink(tl, new User(user))),
|
linkList: transactionLinks.map((tl) => new TransactionLink(tl, new User(user))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.CREATE_CONTRIBUTION_LINK])
|
||||||
|
@Mutation(() => ContributionLink)
|
||||||
|
async createContributionLink(
|
||||||
|
@Args()
|
||||||
|
{
|
||||||
|
amount,
|
||||||
|
name,
|
||||||
|
memo,
|
||||||
|
cycle,
|
||||||
|
validFrom,
|
||||||
|
validTo,
|
||||||
|
maxAmountPerMonth,
|
||||||
|
maxPerCycle,
|
||||||
|
}: ContributionLinkArgs,
|
||||||
|
): Promise<ContributionLink> {
|
||||||
|
const dbContributionLink = new DbContributionLink()
|
||||||
|
dbContributionLink.amount = amount
|
||||||
|
dbContributionLink.name = name
|
||||||
|
dbContributionLink.memo = memo
|
||||||
|
dbContributionLink.createdAt = new Date()
|
||||||
|
dbContributionLink.code = contributionLinkCode(dbContributionLink.createdAt)
|
||||||
|
dbContributionLink.cycle = cycle
|
||||||
|
if (validFrom) dbContributionLink.validFrom = new Date(validFrom)
|
||||||
|
if (validTo) dbContributionLink.validTo = new Date(validTo)
|
||||||
|
dbContributionLink.maxAmountPerMonth = maxAmountPerMonth
|
||||||
|
dbContributionLink.maxPerCycle = maxPerCycle
|
||||||
|
await dbContributionLink.save()
|
||||||
|
return new ContributionLink(dbContributionLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.LIST_CONTRIBUTION_LINKS])
|
||||||
|
@Query(() => ContributionLinkList)
|
||||||
|
async listContributionLinks(
|
||||||
|
@Args()
|
||||||
|
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
||||||
|
): Promise<ContributionLinkList> {
|
||||||
|
const [links, count] = await DbContributionLink.findAndCount({
|
||||||
|
order: { createdAt: order },
|
||||||
|
skip: (currentPage - 1) * pageSize,
|
||||||
|
take: pageSize,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
links: links.map((link: DbContributionLink) => new ContributionLink(link)),
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.DELETE_CONTRIBUTION_LINK])
|
||||||
|
@Mutation(() => Date, { nullable: true })
|
||||||
|
async deleteContributionLink(@Arg('id', () => Int) id: number): Promise<Date | null> {
|
||||||
|
const contributionLink = await DbContributionLink.findOne(id)
|
||||||
|
if (!contributionLink) {
|
||||||
|
logger.error(`Contribution Link not found to given id: ${id}`)
|
||||||
|
throw new Error('Contribution Link not found to given id.')
|
||||||
|
}
|
||||||
|
await contributionLink.softRemove()
|
||||||
|
const newContributionLink = await DbContributionLink.findOne({ id }, { withDeleted: true })
|
||||||
|
return newContributionLink ? newContributionLink.deletedAt : null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.UPDATE_CONTRIBUTION_LINK])
|
||||||
|
@Mutation(() => ContributionLink)
|
||||||
|
async updateContributionLink(
|
||||||
|
@Args()
|
||||||
|
{
|
||||||
|
amount,
|
||||||
|
name,
|
||||||
|
memo,
|
||||||
|
cycle,
|
||||||
|
validFrom,
|
||||||
|
validTo,
|
||||||
|
maxAmountPerMonth,
|
||||||
|
maxPerCycle,
|
||||||
|
}: ContributionLinkArgs,
|
||||||
|
@Arg('id', () => Int) id: number,
|
||||||
|
): Promise<ContributionLink> {
|
||||||
|
const dbContributionLink = await DbContributionLink.findOne(id)
|
||||||
|
if (!dbContributionLink) {
|
||||||
|
logger.error(`Contribution Link not found to given id: ${id}`)
|
||||||
|
throw new Error('Contribution Link not found to given id.')
|
||||||
|
}
|
||||||
|
dbContributionLink.amount = amount
|
||||||
|
dbContributionLink.name = name
|
||||||
|
dbContributionLink.memo = memo
|
||||||
|
dbContributionLink.cycle = cycle
|
||||||
|
if (validFrom) dbContributionLink.validFrom = new Date(validFrom)
|
||||||
|
if (validTo) dbContributionLink.validTo = new Date(validTo)
|
||||||
|
dbContributionLink.maxAmountPerMonth = maxAmountPerMonth
|
||||||
|
dbContributionLink.maxPerCycle = maxPerCycle
|
||||||
|
await dbContributionLink.save()
|
||||||
|
return new ContributionLink(dbContributionLink)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreationMap {
|
interface CreationMap {
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
export interface ContributionLinkInterface {
|
||||||
|
amount: number
|
||||||
|
name: string
|
||||||
|
memo: string
|
||||||
|
validFrom?: Date
|
||||||
|
validTo?: Date
|
||||||
|
}
|
||||||
18
backend/src/seeds/contributionLink/index.ts
Normal file
18
backend/src/seeds/contributionLink/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ContributionLinkInterface } from './ContributionLinkInterface'
|
||||||
|
|
||||||
|
export const contributionLinks: ContributionLinkInterface[] = [
|
||||||
|
{
|
||||||
|
name: 'Dokumenta 2017',
|
||||||
|
memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2017',
|
||||||
|
amount: 200,
|
||||||
|
validFrom: new Date(2017, 3, 8),
|
||||||
|
validTo: new Date(2017, 6, 16),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dokumenta 2022',
|
||||||
|
memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022',
|
||||||
|
amount: 200,
|
||||||
|
validFrom: new Date(2022, 5, 18),
|
||||||
|
validTo: new Date(2022, 8, 25),
|
||||||
|
},
|
||||||
|
]
|
||||||
27
backend/src/seeds/factory/contributionLink.ts
Normal file
27
backend/src/seeds/factory/contributionLink.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||||
|
import { createContributionLink } from '@/seeds/graphql/mutations'
|
||||||
|
import { login } from '@/seeds/graphql/queries'
|
||||||
|
import { ContributionLinkInterface } from '@/seeds/contributionLink/ContributionLinkInterface'
|
||||||
|
|
||||||
|
export const contributionLinkFactory = async (
|
||||||
|
client: ApolloServerTestClient,
|
||||||
|
contributionLink: ContributionLinkInterface,
|
||||||
|
): Promise<void> => {
|
||||||
|
const { mutate, query } = client
|
||||||
|
|
||||||
|
// login as admin
|
||||||
|
await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } })
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
amount: contributionLink.amount,
|
||||||
|
memo: contributionLink.memo,
|
||||||
|
name: contributionLink.name,
|
||||||
|
cycle: 'ONCE',
|
||||||
|
maxPerCycle: 1,
|
||||||
|
maxAmountPerMonth: 200,
|
||||||
|
validFrom: contributionLink.validFrom ? contributionLink.validFrom.toISOString() : undefined,
|
||||||
|
validTo: contributionLink.validTo ? contributionLink.validTo.toISOString() : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
await mutate({ mutation: createContributionLink, variables })
|
||||||
|
}
|
||||||
@ -137,3 +137,85 @@ export const deletePendingCreation = gql`
|
|||||||
deletePendingCreation(id: $id)
|
deletePendingCreation(id: $id)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const createContributionLink = gql`
|
||||||
|
mutation (
|
||||||
|
$amount: Decimal!
|
||||||
|
$name: String!
|
||||||
|
$memo: String!
|
||||||
|
$cycle: String!
|
||||||
|
$validFrom: String
|
||||||
|
$validTo: String
|
||||||
|
$maxAmountPerMonth: Decimal
|
||||||
|
$maxPerCycle: Int! = 1
|
||||||
|
) {
|
||||||
|
createContributionLink(
|
||||||
|
amount: $amount
|
||||||
|
name: $name
|
||||||
|
memo: $memo
|
||||||
|
cycle: $cycle
|
||||||
|
validFrom: $validFrom
|
||||||
|
validTo: $validTo
|
||||||
|
maxAmountPerMonth: $maxAmountPerMonth
|
||||||
|
maxPerCycle: $maxPerCycle
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
amount
|
||||||
|
name
|
||||||
|
memo
|
||||||
|
code
|
||||||
|
link
|
||||||
|
createdAt
|
||||||
|
validFrom
|
||||||
|
validTo
|
||||||
|
maxAmountPerMonth
|
||||||
|
cycle
|
||||||
|
maxPerCycle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const updateContributionLink = gql`
|
||||||
|
mutation (
|
||||||
|
$amount: Decimal!
|
||||||
|
$name: String!
|
||||||
|
$memo: String!
|
||||||
|
$cycle: String!
|
||||||
|
$validFrom: String
|
||||||
|
$validTo: String
|
||||||
|
$maxAmountPerMonth: Decimal
|
||||||
|
$maxPerCycle: Int! = 1
|
||||||
|
$id: Int!
|
||||||
|
) {
|
||||||
|
updateContributionLink(
|
||||||
|
amount: $amount
|
||||||
|
name: $name
|
||||||
|
memo: $memo
|
||||||
|
cycle: $cycle
|
||||||
|
validFrom: $validFrom
|
||||||
|
validTo: $validTo
|
||||||
|
maxAmountPerMonth: $maxAmountPerMonth
|
||||||
|
maxPerCycle: $maxPerCycle
|
||||||
|
id: $id
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
amount
|
||||||
|
name
|
||||||
|
memo
|
||||||
|
code
|
||||||
|
link
|
||||||
|
createdAt
|
||||||
|
validFrom
|
||||||
|
validTo
|
||||||
|
maxAmountPerMonth
|
||||||
|
cycle
|
||||||
|
maxPerCycle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const deleteContributionLink = gql`
|
||||||
|
mutation ($id: Int!) {
|
||||||
|
deleteContributionLink(id: $id)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -217,3 +217,25 @@ export const listTransactionLinksAdmin = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const listContributionLinks = gql`
|
||||||
|
query ($pageSize: Int = 25, $currentPage: Int = 1, $order: Order) {
|
||||||
|
listContributionLinks(pageSize: $pageSize, currentPage: $currentPage, order: $order) {
|
||||||
|
links {
|
||||||
|
id
|
||||||
|
amount
|
||||||
|
name
|
||||||
|
memo
|
||||||
|
code
|
||||||
|
link
|
||||||
|
createdAt
|
||||||
|
validFrom
|
||||||
|
validTo
|
||||||
|
maxAmountPerMonth
|
||||||
|
cycle
|
||||||
|
maxPerCycle
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -9,9 +9,11 @@ import { name, internet, datatype } from 'faker'
|
|||||||
import { users } from './users/index'
|
import { users } from './users/index'
|
||||||
import { creations } from './creation/index'
|
import { creations } from './creation/index'
|
||||||
import { transactionLinks } from './transactionLink/index'
|
import { transactionLinks } from './transactionLink/index'
|
||||||
|
import { contributionLinks } from './contributionLink/index'
|
||||||
import { userFactory } from './factory/user'
|
import { userFactory } from './factory/user'
|
||||||
import { creationFactory } from './factory/creation'
|
import { creationFactory } from './factory/creation'
|
||||||
import { transactionLinkFactory } from './factory/transactionLink'
|
import { transactionLinkFactory } from './factory/transactionLink'
|
||||||
|
import { contributionLinkFactory } from './factory/contributionLink'
|
||||||
import { entities } from '@entity/index'
|
import { entities } from '@entity/index'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
|
|
||||||
@ -77,6 +79,11 @@ const run = async () => {
|
|||||||
await transactionLinkFactory(seedClient, transactionLinks[i])
|
await transactionLinkFactory(seedClient, transactionLinks[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create Contribution Links
|
||||||
|
for (let i = 0; i < contributionLinks.length; i++) {
|
||||||
|
await contributionLinkFactory(seedClient, contributionLinks[i])
|
||||||
|
}
|
||||||
|
|
||||||
await con.close()
|
await con.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,10 +22,11 @@ COMMUNITY_NAME="Gradido Development Stage1"
|
|||||||
COMMUNITY_URL=https://stage1.gradido.net/
|
COMMUNITY_URL=https://stage1.gradido.net/
|
||||||
COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register
|
COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register
|
||||||
COMMUNITY_REDEEM_URL=https://stage1.gradido.net/redeem/{code}
|
COMMUNITY_REDEEM_URL=https://stage1.gradido.net/redeem/{code}
|
||||||
|
COMMUNITY_REDEEM_CONTRIBUTION_URL=https://stage1.gradido.net/redeem/CL-{code}
|
||||||
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
||||||
|
|
||||||
# backend
|
# backend
|
||||||
BACKEND_CONFIG_VERSION=v6.2022-04-21
|
BACKEND_CONFIG_VERSION=v7.2022-06-15
|
||||||
|
|
||||||
JWT_EXPIRES_IN=30m
|
JWT_EXPIRES_IN=30m
|
||||||
GDT_API_URL=https://gdt.gradido.net
|
GDT_API_URL=https://gdt.gradido.net
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user