mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'master' into 2049-remove-image-entries-in-docker-compose-files
This commit is contained in:
commit
3e259b049a
@ -26,6 +26,7 @@ export enum RIGHTS {
|
|||||||
LIST_TRANSACTION_LINKS = 'LIST_TRANSACTION_LINKS',
|
LIST_TRANSACTION_LINKS = 'LIST_TRANSACTION_LINKS',
|
||||||
GDT_BALANCE = 'GDT_BALANCE',
|
GDT_BALANCE = 'GDT_BALANCE',
|
||||||
CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION',
|
CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION',
|
||||||
|
DELETE_CONTRIBUTION = 'DELETE_CONTRIBUTION',
|
||||||
LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS',
|
LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS',
|
||||||
LIST_ALL_CONTRIBUTIONS = 'LIST_ALL_CONTRIBUTIONS',
|
LIST_ALL_CONTRIBUTIONS = 'LIST_ALL_CONTRIBUTIONS',
|
||||||
UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION',
|
UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION',
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export const ROLE_USER = new Role('user', [
|
|||||||
RIGHTS.LIST_TRANSACTION_LINKS,
|
RIGHTS.LIST_TRANSACTION_LINKS,
|
||||||
RIGHTS.GDT_BALANCE,
|
RIGHTS.GDT_BALANCE,
|
||||||
RIGHTS.CREATE_CONTRIBUTION,
|
RIGHTS.CREATE_CONTRIBUTION,
|
||||||
|
RIGHTS.DELETE_CONTRIBUTION,
|
||||||
RIGHTS.LIST_CONTRIBUTIONS,
|
RIGHTS.LIST_CONTRIBUTIONS,
|
||||||
RIGHTS.LIST_ALL_CONTRIBUTIONS,
|
RIGHTS.LIST_ALL_CONTRIBUTIONS,
|
||||||
RIGHTS.UPDATE_CONTRIBUTION,
|
RIGHTS.UPDATE_CONTRIBUTION,
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class Contribution {
|
|||||||
this.deletedAt = contribution.deletedAt
|
this.deletedAt = contribution.deletedAt
|
||||||
this.confirmedAt = contribution.confirmedAt
|
this.confirmedAt = contribution.confirmedAt
|
||||||
this.confirmedBy = contribution.confirmedBy
|
this.confirmedBy = contribution.confirmedBy
|
||||||
|
this.contributionDate = contribution.contributionDate
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
@ -43,6 +44,9 @@ export class Contribution {
|
|||||||
|
|
||||||
@Field(() => Number, { nullable: true })
|
@Field(() => Number, { nullable: true })
|
||||||
confirmedBy: number | null
|
confirmedBy: number | null
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
contributionDate: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
import {
|
import {
|
||||||
adminUpdateContribution,
|
adminUpdateContribution,
|
||||||
|
confirmContribution,
|
||||||
createContribution,
|
createContribution,
|
||||||
|
deleteContribution,
|
||||||
updateContribution,
|
updateContribution,
|
||||||
} from '@/seeds/graphql/mutations'
|
} from '@/seeds/graphql/mutations'
|
||||||
import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries'
|
import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries'
|
||||||
@ -487,6 +489,11 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
it('returns allCreation', async () => {
|
it('returns allCreation', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
query({
|
query({
|
||||||
@ -522,4 +529,129 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('deleteContribution', () => {
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: deleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: -1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await userFactory(testEnv, bibiBloxberg)
|
||||||
|
await userFactory(testEnv, peterLustig)
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
result = await mutate({
|
||||||
|
mutation: createContribution,
|
||||||
|
variables: {
|
||||||
|
amount: 100.0,
|
||||||
|
memo: 'Test env contribution',
|
||||||
|
creationDate: new Date().toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('wrong contribution id', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: deleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: -1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('Contribution not found for given id.')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('other user sends a deleteContribtuion', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: deleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: result.data.createContribution.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('Can not delete contribution of another user')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('User deletes own contribution', () => {
|
||||||
|
it('deletes successfully', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: deleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: result.data.createContribution.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('User deletes already confirmed contribution', () => {
|
||||||
|
it('throws an error', async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
await mutate({
|
||||||
|
mutation: confirmContribution,
|
||||||
|
variables: {
|
||||||
|
id: result.data.createContribution.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: deleteContribution,
|
||||||
|
variables: {
|
||||||
|
id: result.data.createContribution.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('A confirmed contribution can not be deleted')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -38,6 +38,27 @@ export class ContributionResolver {
|
|||||||
return new UnconfirmedContribution(contribution, user, creations)
|
return new UnconfirmedContribution(contribution, user, creations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.DELETE_CONTRIBUTION])
|
||||||
|
@Mutation(() => Boolean)
|
||||||
|
async deleteContribution(
|
||||||
|
@Arg('id', () => Int) id: number,
|
||||||
|
@Ctx() context: Context,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const user = getUser(context)
|
||||||
|
const contribution = await dbContribution.findOne(id)
|
||||||
|
if (!contribution) {
|
||||||
|
throw new Error('Contribution not found for given id.')
|
||||||
|
}
|
||||||
|
if (contribution.userId !== user.id) {
|
||||||
|
throw new Error('Can not delete contribution of another user')
|
||||||
|
}
|
||||||
|
if (contribution.confirmedAt) {
|
||||||
|
throw new Error('A confirmed contribution can not be deleted')
|
||||||
|
}
|
||||||
|
const res = await contribution.softRemove()
|
||||||
|
return !!res
|
||||||
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.LIST_CONTRIBUTIONS])
|
@Authorized([RIGHTS.LIST_CONTRIBUTIONS])
|
||||||
@Query(() => ContributionListResult)
|
@Query(() => ContributionListResult)
|
||||||
async listContributions(
|
async listContributions(
|
||||||
|
|||||||
@ -255,3 +255,9 @@ export const updateContribution = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const deleteContribution = gql`
|
||||||
|
mutation ($id: Int!) {
|
||||||
|
deleteContribution(id: $id)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
83
database/entity/0039-contributions_table/User.ts
Normal file
83
database/entity/0039-contributions_table/User.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
BaseEntity,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
DeleteDateColumn,
|
||||||
|
OneToMany,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm'
|
||||||
|
import { Contribution } from '../Contribution'
|
||||||
|
|
||||||
|
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
|
export class User extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
pubKey: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||||
|
privKey: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'first_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'last_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
lastName: string
|
||||||
|
|
||||||
|
@DeleteDateColumn()
|
||||||
|
deletedAt: Date | null
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
|
password: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
emailHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
|
emailChecked: boolean
|
||||||
|
|
||||||
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
|
language: string
|
||||||
|
|
||||||
|
@Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null })
|
||||||
|
isAdmin: Date | null
|
||||||
|
|
||||||
|
@Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null })
|
||||||
|
referrerId?: number | null
|
||||||
|
|
||||||
|
@Column({ name: 'publisher_id', default: 0 })
|
||||||
|
publisherId: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
name: 'passphrase',
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@OneToMany(() => Contribution, (contribution) => contribution.user)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
contributions?: Contribution[]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user