mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'master' into 2948-frontend_sendcoind_community_selection
This commit is contained in:
commit
52abda3479
@ -1,18 +1,28 @@
|
||||
import { IsEmail, MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field, InputType } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { isValidDateString } from '@/graphql/validator/DateString'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@InputType()
|
||||
@ArgsType()
|
||||
export class AdminCreateContributionArgs {
|
||||
@Field(() => String)
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
|
||||
@Field(() => String)
|
||||
@isValidDateString()
|
||||
creationDate: string
|
||||
}
|
||||
|
||||
@ -1,17 +1,27 @@
|
||||
import { IsPositive, MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { isValidDateString } from '@/graphql/validator/DateString'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@ArgsType()
|
||||
export class AdminUpdateContributionArgs {
|
||||
@Field(() => Int)
|
||||
@IsPositive()
|
||||
id: number
|
||||
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
|
||||
@Field(() => String)
|
||||
@isValidDateString()
|
||||
creationDate: string
|
||||
}
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
import { MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field, InputType } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { isValidDateString } from '@/graphql/validator/DateString'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@InputType()
|
||||
@ArgsType()
|
||||
export class ContributionArgs {
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
|
||||
@Field(() => String)
|
||||
@isValidDateString()
|
||||
creationDate: string
|
||||
}
|
||||
|
||||
@ -1,29 +1,49 @@
|
||||
import { IsPositive, IsString, MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
import {
|
||||
MEMO_MAX_CHARS,
|
||||
MEMO_MIN_CHARS,
|
||||
CONTRIBUTIONLINK_NAME_MIN_CHARS,
|
||||
CONTRIBUTIONLINK_NAME_MAX_CHARS,
|
||||
} from '@/graphql/resolver/const/const'
|
||||
import { isValidDateString } from '@/graphql/validator/DateString'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@ArgsType()
|
||||
export class ContributionLinkArgs {
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(CONTRIBUTIONLINK_NAME_MAX_CHARS)
|
||||
@MinLength(CONTRIBUTIONLINK_NAME_MIN_CHARS)
|
||||
name: string
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
cycle: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@isValidDateString()
|
||||
validFrom?: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@isValidDateString()
|
||||
validTo?: string | null
|
||||
|
||||
@Field(() => Decimal, { nullable: true })
|
||||
@IsPositiveDecimal()
|
||||
maxAmountPerMonth?: Decimal | null
|
||||
|
||||
@Field(() => Int)
|
||||
@IsPositive()
|
||||
maxPerCycle: number
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { IsInt, IsString, IsEnum } from 'class-validator'
|
||||
import { ArgsType, Field, Int, InputType } from 'type-graphql'
|
||||
|
||||
import { ContributionMessageType } from '@enum/ContributionMessageType'
|
||||
@ -6,11 +7,14 @@ import { ContributionMessageType } from '@enum/ContributionMessageType'
|
||||
@ArgsType()
|
||||
export class ContributionMessageArgs {
|
||||
@Field(() => Int)
|
||||
@IsInt()
|
||||
contributionId: number
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
message: string
|
||||
|
||||
@Field(() => ContributionMessageType, { defaultValue: ContributionMessageType.DIALOG })
|
||||
@IsEnum(ContributionMessageType)
|
||||
messageType: ContributionMessageType
|
||||
}
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
import { IsEmail, IsInt, IsString } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class CreateUserArgs {
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
alias?: string | null
|
||||
|
||||
@Field(() => String)
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
lastName: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
language?: string | null
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
@IsInt()
|
||||
publisherId?: number | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
redeemCode?: string | null
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
/* eslint-disable type-graphql/invalid-nullable-input-type */
|
||||
import { IsPositive, IsEnum } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { Order } from '@enum/Order'
|
||||
@ -6,11 +7,14 @@ import { Order } from '@enum/Order'
|
||||
@ArgsType()
|
||||
export class Paginated {
|
||||
@Field(() => Int, { defaultValue: 1 })
|
||||
@IsPositive()
|
||||
currentPage: number
|
||||
|
||||
@Field(() => Int, { defaultValue: 3 })
|
||||
@IsPositive()
|
||||
pageSize: number
|
||||
|
||||
@Field(() => Order, { defaultValue: Order.DESC })
|
||||
@IsEnum(Order)
|
||||
order: Order
|
||||
}
|
||||
|
||||
@ -1,18 +1,25 @@
|
||||
import { IsBoolean, IsPositive, IsString } from 'class-validator'
|
||||
import { Field, ArgsType, Int } from 'type-graphql'
|
||||
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
|
||||
import { isContributionStatusArray } from '@/graphql/validator/ContributionStatusArray'
|
||||
|
||||
@ArgsType()
|
||||
export class SearchContributionsFilterArgs {
|
||||
@Field(() => [ContributionStatus], { nullable: true, defaultValue: null })
|
||||
@isContributionStatusArray()
|
||||
statusFilter?: ContributionStatus[] | null
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
@IsPositive()
|
||||
userId?: number | null
|
||||
|
||||
@Field(() => String, { nullable: true, defaultValue: '' })
|
||||
@IsString()
|
||||
query?: string | null
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
@IsBoolean()
|
||||
noHashtag?: boolean | null
|
||||
}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import { IsBoolean } from 'class-validator'
|
||||
import { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
export class SearchUsersFilters {
|
||||
@Field(() => Boolean, { nullable: true, defaultValue: null })
|
||||
@IsBoolean()
|
||||
byActivated?: boolean | null
|
||||
|
||||
@Field(() => Boolean, { nullable: true, defaultValue: null })
|
||||
@IsBoolean()
|
||||
byDeleted?: boolean | null
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { IsPositive, IsEnum } from 'class-validator'
|
||||
import { ArgsType, Field, Int, InputType } from 'type-graphql'
|
||||
|
||||
import { RoleNames } from '@enum/RoleNames'
|
||||
@ -6,8 +7,10 @@ import { RoleNames } from '@enum/RoleNames'
|
||||
@ArgsType()
|
||||
export class SetUserRoleArgs {
|
||||
@Field(() => Int)
|
||||
@IsPositive()
|
||||
userId: number
|
||||
|
||||
@Field(() => RoleNames, { nullable: true })
|
||||
@IsEnum(RoleNames)
|
||||
role: RoleNames | null | undefined
|
||||
}
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { MaxLength, MinLength } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionLinkArgs {
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
}
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
/* eslint-disable type-graphql/invalid-nullable-input-type */
|
||||
import { IsBoolean } from 'class-validator'
|
||||
import { Field, InputType } from 'type-graphql'
|
||||
|
||||
@InputType()
|
||||
export class TransactionLinkFilters {
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
@IsBoolean()
|
||||
withDeleted?: boolean
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
@IsBoolean()
|
||||
withExpired?: boolean
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
@IsBoolean()
|
||||
withRedeemed?: boolean
|
||||
}
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
import { MaxLength, MinLength, IsString } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql/resolver/const/const'
|
||||
import { IsPositiveDecimal } from '@/graphql/validator/Decimal'
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionSendArgs {
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
identifier: string
|
||||
|
||||
@Field(() => Decimal)
|
||||
@IsPositiveDecimal()
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo: string
|
||||
|
||||
@Field(() => Int)
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import { IsEmail, IsInt, IsString } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class UnsecureLoginArgs {
|
||||
@Field(() => String)
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
@IsString()
|
||||
password: string
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
@IsInt()
|
||||
publisherId?: number | null
|
||||
}
|
||||
|
||||
@ -1,31 +1,41 @@
|
||||
import { IsBoolean, IsInt, IsString } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class UpdateUserInfosArgs {
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
firstName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
lastName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
alias?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
language?: string
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
@IsInt()
|
||||
publisherId?: number | null
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
password?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
passwordNew?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsBoolean()
|
||||
hideAmountGDD?: boolean
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsBoolean()
|
||||
hideAmountGDT?: boolean
|
||||
}
|
||||
|
||||
@ -339,107 +339,142 @@ describe('Contribution Links', () => {
|
||||
|
||||
it('returns an error if name is shorter than 5 characters', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
name: '123',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
name: '123',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'name',
|
||||
constraints: {
|
||||
minLength: 'name must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('The value of name is too short')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "The value of name is too short"', () => {
|
||||
expect(logger.error).toBeCalledWith('The value of name is too short', 3)
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns an error if name is longer than 100 characters', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
name: '12345678901234567892123456789312345678941234567895123456789612345678971234567898123456789912345678901',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
name: '12345678901234567892123456789312345678941234567895123456789612345678971234567898123456789912345678901',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'name',
|
||||
constraints: {
|
||||
maxLength: 'name must be shorter than or equal to 100 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('The value of name is too long')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "The value of name is too long"', () => {
|
||||
expect(logger.error).toBeCalledWith('The value of name is too long', 101)
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns an error if memo is shorter than 5 characters', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
memo: '123',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
memo: '123',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
minLength: 'memo must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('The value of memo is too short')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "The value of memo is too short"', () => {
|
||||
expect(logger.error).toBeCalledWith('The value of memo is too short', 3)
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns an error if memo is longer than 255 characters', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
memo: '1234567890123456789212345678931234567894123456789512345678961234567897123456789812345678991234567890123456789012345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678901234567892123456789312345678941234567895123456',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
memo: '1234567890123456789212345678931234567894123456789512345678961234567897123456789812345678991234567890123456789012345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678901234567892123456789312345678941234567895123456',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
maxLength: 'memo must be shorter than or equal to 255 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('The value of memo is too long')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "The value of memo is too long"', () => {
|
||||
expect(logger.error).toBeCalledWith('The value of memo is too long', 256)
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns an error if amount is not positive', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
amount: new Decimal(0),
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
...variables,
|
||||
amount: new Decimal(0),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'amount',
|
||||
constraints: {
|
||||
isPositiveDecimal: 'The amount must be a positive value amount',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('The amount must be a positiv value')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "The amount must be a positiv value"', () => {
|
||||
expect(logger.error).toBeCalledWith('The amount must be a positiv value', new Decimal(0))
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { MoreThan, IsNull } from '@dbTools/typeorm'
|
||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Resolver, Args, Arg, Authorized, Mutation, Query, Int, Ctx } from 'type-graphql'
|
||||
|
||||
import { ContributionLinkArgs } from '@arg/ContributionLinkArgs'
|
||||
@ -18,12 +17,6 @@ import {
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import {
|
||||
CONTRIBUTIONLINK_NAME_MAX_CHARS,
|
||||
CONTRIBUTIONLINK_NAME_MIN_CHARS,
|
||||
MEMO_MAX_CHARS,
|
||||
MEMO_MIN_CHARS,
|
||||
} from './const/const'
|
||||
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
|
||||
import { isStartEndDateValid } from './util/creations'
|
||||
|
||||
@ -46,21 +39,6 @@ export class ContributionLinkResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<ContributionLink> {
|
||||
isStartEndDateValid(validFrom, validTo)
|
||||
if (name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS) {
|
||||
throw new LogError('The value of name is too short', name.length)
|
||||
}
|
||||
if (name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS) {
|
||||
throw new LogError('The value of name is too long', name.length)
|
||||
}
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
throw new LogError('The value of memo is too short', memo.length)
|
||||
}
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('The value of memo is too long', memo.length)
|
||||
}
|
||||
if (!new Decimal(amount).isPositive()) {
|
||||
throw new LogError('The amount must be a positiv value', amount)
|
||||
}
|
||||
|
||||
const dbContributionLink = new DbContributionLink()
|
||||
dbContributionLink.amount = amount
|
||||
|
||||
@ -201,6 +201,7 @@ describe('ContributionResolver', () => {
|
||||
it('throws error when memo length smaller than 5 chars', async () => {
|
||||
jest.clearAllMocks()
|
||||
const date = new Date()
|
||||
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContribution,
|
||||
variables: {
|
||||
@ -209,12 +210,23 @@ describe('ContributionResolver', () => {
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
|
||||
expect(errorObjects).toEqual([new GraphQLError('Memo text is too short')])
|
||||
})
|
||||
|
||||
it('logs the error "Memo text is too short"', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too short', 4)
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
minLength: 'memo must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throws error when memo length greater than 255 chars', async () => {
|
||||
@ -228,11 +240,23 @@ describe('ContributionResolver', () => {
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([new GraphQLError('Memo text is too long')])
|
||||
})
|
||||
|
||||
it('logs the error "Memo text is too long"', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too long', 259)
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
maxLength: 'memo must be shorter than or equal to 255 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throws error when creationDate not-valid', async () => {
|
||||
@ -245,27 +269,35 @@ describe('ContributionResolver', () => {
|
||||
creationDate: 'not-valid',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([
|
||||
new GraphQLError('No information for available creations for the given date'),
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'creationDate',
|
||||
constraints: {
|
||||
isValidDateString: 'creationDate must be a valid date string, creationDate',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('logs the error "No information for available creations for the given date"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No information for available creations for the given date',
|
||||
expect.any(Date),
|
||||
)
|
||||
})
|
||||
|
||||
it('throws error when creationDate 3 month behind', async () => {
|
||||
jest.clearAllMocks()
|
||||
const date = new Date()
|
||||
date.setMonth(date.getMonth() - 3)
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createContribution,
|
||||
variables: {
|
||||
amount: 100.0,
|
||||
memo: 'Test env contribution',
|
||||
creationDate: date.setMonth(date.getMonth() - 3).toString(),
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([
|
||||
@ -346,11 +378,23 @@ describe('ContributionResolver', () => {
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([new GraphQLError('Memo text is too short')])
|
||||
})
|
||||
|
||||
it('logs the error "Memo text is too short"', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too short', 4)
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
minLength: 'memo must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@ -367,11 +411,23 @@ describe('ContributionResolver', () => {
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([new GraphQLError('Memo text is too long')])
|
||||
})
|
||||
|
||||
it('logs the error "Memo text is too long"', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too long', 259)
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
maxLength: 'memo must be shorter than or equal to 255 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@ -551,13 +607,14 @@ describe('ContributionResolver', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
const date = new Date()
|
||||
date.setMonth(date.getMonth() - 3)
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: updateContribution,
|
||||
variables: {
|
||||
contributionId: pendingContribution.data.createContribution.id,
|
||||
amount: 10.0,
|
||||
memo: 'Test env contribution',
|
||||
creationDate: date.setMonth(date.getMonth() - 3).toString(),
|
||||
creationDate: date.toString(),
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toEqual([
|
||||
@ -1979,17 +2036,28 @@ describe('ContributionResolver', () => {
|
||||
describe('date of creation is not a date string', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({ mutation: adminCreateContribution, variables }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('CreationDate is invalid')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error "CreationDate is invalid"', () => {
|
||||
expect(logger.error).toBeCalledWith('CreationDate is invalid', 'invalid-date')
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: adminCreateContribution,
|
||||
variables,
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'creationDate',
|
||||
constraints: {
|
||||
isValidDateString:
|
||||
'creationDate must be a valid date string, creationDate',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@ -2181,7 +2249,7 @@ describe('ContributionResolver', () => {
|
||||
mutate({
|
||||
mutation: adminUpdateContribution,
|
||||
variables: {
|
||||
id: -1,
|
||||
id: 728,
|
||||
amount: new Decimal(300),
|
||||
memo: 'Danke Bibi!',
|
||||
creationDate: contributionDateFormatter(new Date()),
|
||||
@ -2195,7 +2263,7 @@ describe('ContributionResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution not found"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', -1)
|
||||
expect(logger.error).toBeCalledWith('Contribution not found', 728)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2732,6 +2800,24 @@ describe('ContributionResolver', () => {
|
||||
resetToken()
|
||||
})
|
||||
|
||||
it('throw error for invalid ContributionStatus in statusFilter array', async () => {
|
||||
const { errors: errorObjects } = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
statusFilter: ['INVALID_STATUS'],
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message:
|
||||
'Variable "$statusFilter" got invalid value "INVALID_STATUS" at "statusFilter[0]"; Value "INVALID_STATUS" does not exist in "ContributionStatus" enum.',
|
||||
extensions: {
|
||||
code: 'BAD_USER_INPUT',
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('returns 18 creations in total', async () => {
|
||||
const {
|
||||
data: { adminListContributions: contributionListObject },
|
||||
|
||||
@ -45,12 +45,10 @@ import { calculateDecay } from '@/util/decay'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { fullName } from '@/util/utilities'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||
import {
|
||||
getUserCreation,
|
||||
validateContribution,
|
||||
updateCreations,
|
||||
isValidDateString,
|
||||
getOpenCreations,
|
||||
} from './util/creations'
|
||||
import { findContributions } from './util/findContributions'
|
||||
@ -66,12 +64,6 @@ export class ContributionResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<UnconfirmedContribution> {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
throw new LogError('Memo text is too short', memo.length)
|
||||
}
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('Memo text is too long', memo.length)
|
||||
}
|
||||
|
||||
const user = getUser(context)
|
||||
const creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
@ -181,12 +173,6 @@ export class ContributionResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<UnconfirmedContribution> {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
throw new LogError('Memo text is too short', memo.length)
|
||||
}
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('Memo text is too long', memo.length)
|
||||
}
|
||||
|
||||
const user = getUser(context)
|
||||
|
||||
@ -264,9 +250,6 @@ export class ContributionResolver {
|
||||
`adminCreateContribution(email=${email}, amount=${amount.toString()}, memo=${memo}, creationDate=${creationDate})`,
|
||||
)
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
if (!isValidDateString(creationDate)) {
|
||||
throw new LogError('CreationDate is invalid', creationDate)
|
||||
}
|
||||
const emailContact = await UserContact.findOne({
|
||||
where: { email },
|
||||
withDeleted: true,
|
||||
|
||||
@ -94,38 +94,115 @@ describe('TransactionLinkResolver', () => {
|
||||
|
||||
it('throws error when amount is zero', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: 0,
|
||||
memo: 'Test',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [new GraphQLError('Amount must be a positive number')],
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: 0,
|
||||
memo: 'Test Test',
|
||||
},
|
||||
})
|
||||
})
|
||||
it('logs the error "Amount must be a positive number" - 0', () => {
|
||||
expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(0))
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'amount',
|
||||
constraints: {
|
||||
isPositiveDecimal: 'The amount must be a positive value amount',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throws error when amount is negative', async () => {
|
||||
jest.clearAllMocks()
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: -10,
|
||||
memo: 'Test',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [new GraphQLError('Amount must be a positive number')],
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: -10,
|
||||
memo: 'Test Test',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'amount',
|
||||
constraints: {
|
||||
isPositiveDecimal: 'The amount must be a positive value amount',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
it('logs the error "Amount must be a positive number" - -10', () => {
|
||||
expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(-10))
|
||||
|
||||
it('throws error when memo text is too short', async () => {
|
||||
jest.clearAllMocks()
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: 100,
|
||||
memo: 'Test',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
minLength: 'memo must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throws error when memo text is too long', async () => {
|
||||
jest.clearAllMocks()
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: 100,
|
||||
memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
maxLength: 'memo must be shorter than or equal to 255 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throws error when user has not enough GDD', async () => {
|
||||
@ -135,7 +212,7 @@ describe('TransactionLinkResolver', () => {
|
||||
mutation: createTransactionLink,
|
||||
variables: {
|
||||
amount: 1001,
|
||||
memo: 'Test',
|
||||
memo: 'Test Test',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
|
||||
@ -74,10 +74,6 @@ export class TransactionLinkResolver {
|
||||
const createdDate = new Date()
|
||||
const validUntil = transactionLinkExpireDate(createdDate)
|
||||
|
||||
if (amount.lessThanOrEqualTo(0)) {
|
||||
throw new LogError('Amount must be a positive number', amount)
|
||||
}
|
||||
|
||||
const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay)
|
||||
|
||||
// validate amount
|
||||
|
||||
@ -7,7 +7,6 @@ import { Event as DbEvent } from '@entity/Event'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { User } from '@entity/User'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
@ -94,7 +93,7 @@ describe('send coins', () => {
|
||||
variables: {
|
||||
identifier: 'wrong@email.com',
|
||||
amount: 100,
|
||||
memo: 'test',
|
||||
memo: 'test test',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
@ -122,7 +121,7 @@ describe('send coins', () => {
|
||||
variables: {
|
||||
identifier: 'stephen@hawking.uk',
|
||||
amount: 100,
|
||||
memo: 'test',
|
||||
memo: 'test test',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
@ -151,7 +150,7 @@ describe('send coins', () => {
|
||||
variables: {
|
||||
identifier: 'garrick@ollivander.com',
|
||||
amount: 100,
|
||||
memo: 'test',
|
||||
memo: 'test test',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
@ -187,7 +186,7 @@ describe('send coins', () => {
|
||||
variables: {
|
||||
identifier: 'bob@baumeister.de',
|
||||
amount: 100,
|
||||
memo: 'test',
|
||||
memo: 'test test',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
@ -205,48 +204,62 @@ describe('send coins', () => {
|
||||
describe('memo text is too short', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: 100,
|
||||
memo: 'test',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: 100,
|
||||
memo: 'Test',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
minLength: 'memo must be longer than or equal to 5 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Memo text is too short')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too short', 4)
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('memo text is too long', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: 100,
|
||||
memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: 100,
|
||||
memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'memo',
|
||||
constraints: {
|
||||
maxLength: 'memo must be shorter than or equal to 255 characters',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Memo text is too long')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Memo text is too long', 256)
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@ -303,24 +316,31 @@ describe('send coins', () => {
|
||||
describe('trying to send negative amount', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
expect(
|
||||
await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: -50,
|
||||
memo: 'testing negative',
|
||||
const { errors: errorObjects } = await mutate({
|
||||
mutation: sendCoins,
|
||||
variables: {
|
||||
identifier: 'peter@lustig.de',
|
||||
amount: -50,
|
||||
memo: 'testing negative',
|
||||
},
|
||||
})
|
||||
expect(errorObjects).toMatchObject([
|
||||
{
|
||||
message: 'Argument Validation Error',
|
||||
extensions: {
|
||||
exception: {
|
||||
validationErrors: [
|
||||
{
|
||||
property: 'amount',
|
||||
constraints: {
|
||||
isPositiveDecimal: 'The amount must be a positive value amount',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Amount to send must be positive')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Amount to send must be positive', new Decimal(-50))
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -33,7 +33,6 @@ import { calculateBalance } from '@/util/validate'
|
||||
import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions'
|
||||
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { getTransactionList } from './util/getTransactionList'
|
||||
@ -56,14 +55,6 @@ export const executeTransaction = async (
|
||||
throw new LogError('Sender and Recipient are the same', sender.id)
|
||||
}
|
||||
|
||||
if (memo.length < MEMO_MIN_CHARS) {
|
||||
throw new LogError('Memo text is too short', memo.length)
|
||||
}
|
||||
|
||||
if (memo.length > MEMO_MAX_CHARS) {
|
||||
throw new LogError('Memo text is too long', memo.length)
|
||||
}
|
||||
|
||||
// validate amount
|
||||
const receivedCallDate = new Date()
|
||||
const sendBalance = await calculateBalance(
|
||||
@ -321,9 +312,6 @@ export class TransactionResolver {
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
logger.info(`sendCoins(identifier=${identifier}, amount=${amount}, memo=${memo})`)
|
||||
if (amount.lte(0)) {
|
||||
throw new LogError('Amount to send must be positive', amount)
|
||||
}
|
||||
|
||||
const senderUser = getUser(context)
|
||||
|
||||
|
||||
@ -12,5 +12,13 @@ export const schema = async (): Promise<GraphQLSchema> => {
|
||||
resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)],
|
||||
authChecker: isAuthorized,
|
||||
scalarsMap: [{ type: Decimal, scalar: DecimalScalar }],
|
||||
validate: {
|
||||
validationError: { target: false },
|
||||
skipMissingProperties: true,
|
||||
skipNullProperties: true,
|
||||
skipUndefinedProperties: false,
|
||||
forbidUnknownValues: true,
|
||||
stopAtFirstError: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
21
backend/src/graphql/validator/ContributionStatusArray.ts
Normal file
21
backend/src/graphql/validator/ContributionStatusArray.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { registerDecorator, ValidationOptions } from 'class-validator'
|
||||
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
|
||||
export function isContributionStatusArray(validationOptions?: ValidationOptions) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isContributionStatusArray',
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: ContributionStatus[]): boolean {
|
||||
const validValues = Object.values(ContributionStatus)
|
||||
return value.every((item) => validValues.includes(item))
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
21
backend/src/graphql/validator/DateString.ts
Normal file
21
backend/src/graphql/validator/DateString.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'
|
||||
|
||||
export function isValidDateString(validationOptions?: ValidationOptions) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isValidDateString',
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: string) {
|
||||
return new Date(value).toString() !== 'Invalid Date'
|
||||
},
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `${propertyName} must be a valid date string, ${args.property}`
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
22
backend/src/graphql/validator/Decimal.ts
Normal file
22
backend/src/graphql/validator/Decimal.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
export function IsPositiveDecimal(validationOptions?: ValidationOptions) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isPositiveDecimal',
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: Decimal) {
|
||||
return value.greaterThan(0)
|
||||
},
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `The ${propertyName} must be a positive value ${args.property}`
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user