mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
refactor contribution resolver for pre-mysql-query optimization based on requested fields
This commit is contained in:
parent
f6c78c59ec
commit
3d20019c02
@ -1,13 +1,10 @@
|
||||
#import './fragments.graphql'
|
||||
|
||||
query adminListContributions(
|
||||
$filter: SearchContributionsFilterArgs!
|
||||
$filter: SearchContributionsFilterArgs
|
||||
$paginated: Paginated
|
||||
) {
|
||||
adminListContributions(
|
||||
paginated: $paginated,
|
||||
filter: $filter
|
||||
) {
|
||||
adminListContributions(paginated: $paginated, filter: $filter) {
|
||||
contributionCount
|
||||
contributionList {
|
||||
id
|
||||
@ -41,7 +38,7 @@ query adminListContributions(
|
||||
}
|
||||
|
||||
query adminListContributionsShort(
|
||||
$filter: SearchContributionsFilterArgs!
|
||||
$filter: SearchContributionsFilterArgs
|
||||
$paginated: Paginated
|
||||
) {
|
||||
adminListContributions(
|
||||
@ -63,7 +60,7 @@ query adminListContributionsShort(
|
||||
|
||||
|
||||
query adminListContributionsCount(
|
||||
$filter: SearchContributionsFilterArgs!
|
||||
$filter: SearchContributionsFilterArgs
|
||||
) {
|
||||
adminListContributions(
|
||||
filter: $filter
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
"gradido-config": "file:../config",
|
||||
"gradido-database": "file:../database",
|
||||
"graphql": "^15.5.1",
|
||||
"graphql-parse-resolve-info": "^4.13.0",
|
||||
"graphql-request": "5.0.0",
|
||||
"graphql-type-json": "0.3.2",
|
||||
"helmet": "^5.1.1",
|
||||
|
||||
@ -17,4 +17,5 @@ export const MODERATOR_RIGHTS = [
|
||||
RIGHTS.ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES,
|
||||
RIGHTS.DENY_CONTRIBUTION,
|
||||
RIGHTS.ADMIN_OPEN_CREATIONS,
|
||||
RIGHTS.VIEW_USER_CONTACT,
|
||||
]
|
||||
|
||||
@ -42,6 +42,7 @@ export enum RIGHTS {
|
||||
HUMHUB_AUTO_LOGIN = 'HUMHUB_AUTO_LOGIN',
|
||||
PROJECT_BRANDING_VIEW = 'PROJECT_BRANDING_VIEW',
|
||||
LIST_HUMHUB_SPACES = 'LIST_HUMHUB_SPACES',
|
||||
VIEW_OWN_USER_CONTACT = 'VIEW_OWN_USER_CONTACT',
|
||||
// Moderator
|
||||
SEARCH_USERS = 'SEARCH_USERS',
|
||||
ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION',
|
||||
@ -59,6 +60,7 @@ export enum RIGHTS {
|
||||
DENY_CONTRIBUTION = 'DENY_CONTRIBUTION',
|
||||
ADMIN_OPEN_CREATIONS = 'ADMIN_OPEN_CREATIONS',
|
||||
ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES = 'ADMIN_LIST_ALL_CONTRIBUTION_MESSAGES',
|
||||
VIEW_USER_CONTACT = 'VIEW_USER_CONTACT',
|
||||
// Moderator AI
|
||||
AI_SEND_MESSAGE = 'AI_SEND_MESSAGE',
|
||||
// Admin
|
||||
|
||||
@ -33,4 +33,5 @@ export const USER_RIGHTS = [
|
||||
RIGHTS.HUMHUB_AUTO_LOGIN,
|
||||
RIGHTS.PROJECT_BRANDING_VIEW,
|
||||
RIGHTS.LIST_HUMHUB_SPACES,
|
||||
RIGHTS.VIEW_USER_CONTACT,
|
||||
]
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
/* eslint-disable type-graphql/invalid-nullable-input-type */
|
||||
import { IsPositive, IsEnum } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
import { ArgsType, Field, Int, InputType } from 'type-graphql'
|
||||
|
||||
import { Order } from '@enum/Order'
|
||||
|
||||
@ArgsType()
|
||||
@InputType()
|
||||
export class Paginated {
|
||||
@Field(() => Int, { nullable: true })
|
||||
@Field(() => Int)
|
||||
@IsPositive()
|
||||
currentPage?: number
|
||||
currentPage: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
@Field(() => Int)
|
||||
@IsPositive()
|
||||
pageSize?: number
|
||||
pageSize: number
|
||||
|
||||
@Field(() => Order, { nullable: true })
|
||||
@Field(() => Order)
|
||||
@IsEnum(Order)
|
||||
order?: Order
|
||||
order: Order
|
||||
|
||||
public constructor(pageSize?: number, currentPage?: number, order?: Order) {
|
||||
this.pageSize = pageSize ?? 3
|
||||
this.currentPage = currentPage ?? 1
|
||||
this.order = order ?? Order.DESC
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { IsBoolean, IsPositive, IsString } from 'class-validator'
|
||||
import { Field, ArgsType, Int } from 'type-graphql'
|
||||
import { Field, ArgsType, Int, InputType } from 'type-graphql'
|
||||
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
|
||||
import { isContributionStatusArray } from '@/graphql/validator/ContributionStatusArray'
|
||||
|
||||
@ArgsType()
|
||||
@InputType()
|
||||
export class SearchContributionsFilterArgs {
|
||||
@Field(() => [ContributionStatus], { nullable: true, defaultValue: null })
|
||||
@isContributionStatusArray()
|
||||
|
||||
@ -1,24 +1,16 @@
|
||||
import { Contribution as dbContribution } from '@entity/Contribution'
|
||||
import { User } from '@entity/User'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
import { PublishNameType } from '@enum/PublishNameType'
|
||||
|
||||
import { PublishNameLogic } from '@/data/PublishName.logic'
|
||||
import { User } from './User'
|
||||
|
||||
@ObjectType()
|
||||
export class Contribution {
|
||||
constructor(contribution: dbContribution, user?: User | null) {
|
||||
constructor(contribution: dbContribution, user?: DbUser | null) {
|
||||
this.id = contribution.id
|
||||
this.firstName = user?.firstName ?? null
|
||||
this.lastName = user?.lastName ?? null
|
||||
this.email = user?.emailContact?.email ?? null
|
||||
this.username = user?.alias ?? null
|
||||
if (user) {
|
||||
const publishNameLogic = new PublishNameLogic(user)
|
||||
this.humhubUsername = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType)
|
||||
}
|
||||
this.amount = contribution.amount
|
||||
this.memo = contribution.memo
|
||||
this.createdAt = contribution.createdAt
|
||||
@ -36,26 +28,26 @@ export class Contribution {
|
||||
this.moderatorId = contribution.moderatorId
|
||||
this.userId = contribution.userId
|
||||
this.resubmissionAt = contribution.resubmissionAt
|
||||
if (user) {
|
||||
this.user = new User(user)
|
||||
}
|
||||
}
|
||||
|
||||
@Field(() => Int)
|
||||
id: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
userId: number | null
|
||||
|
||||
@Field(() => User, { nullable: true })
|
||||
user: User | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
firstName: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
lastName: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
email: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
username: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
humhubUsername: string | null
|
||||
|
||||
@Field(() => Decimal)
|
||||
amount: Decimal
|
||||
|
||||
@ -101,9 +93,6 @@ export class Contribution {
|
||||
@Field(() => Int, { nullable: true })
|
||||
moderatorId: number | null
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
userId: number | null
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
resubmissionAt: Date | null
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import { ObjectType, Field, Int } from 'type-graphql'
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
import { PublishNameType } from '@enum/PublishNameType'
|
||||
|
||||
import { PublishNameLogic } from '@/data/PublishName.logic'
|
||||
|
||||
import { KlickTipp } from './KlickTipp'
|
||||
import { UserContact } from './UserContact'
|
||||
|
||||
@ObjectType()
|
||||
export class User {
|
||||
@ -18,8 +21,13 @@ export class User {
|
||||
}
|
||||
this.gradidoID = user.gradidoID
|
||||
this.alias = user.alias
|
||||
|
||||
const publishNameLogic = new PublishNameLogic(user)
|
||||
this.humhubUsername = publishNameLogic.getUsername(user.humhubPublishName as PublishNameType)
|
||||
|
||||
if (user.emailContact) {
|
||||
this.emailChecked = user.emailContact.emailChecked
|
||||
this.emailContact = new UserContact(user.emailContact)
|
||||
}
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
@ -58,6 +66,9 @@ export class User {
|
||||
@Field(() => String, { nullable: true })
|
||||
alias: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
humhubUsername: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
firstName: string | null
|
||||
|
||||
@ -109,4 +120,7 @@ export class User {
|
||||
|
||||
@Field(() => [String])
|
||||
roles: string[]
|
||||
|
||||
@Field(() => UserContact, { nullable: true })
|
||||
emailContact: UserContact | null
|
||||
}
|
||||
|
||||
42
backend/src/graphql/model/UserContact.ts
Normal file
42
backend/src/graphql/model/UserContact.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { UserContact as DbUserContact } from '@entity/UserContact'
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class UserContact {
|
||||
constructor(userContact: DbUserContact) {
|
||||
Object.assign(this, userContact)
|
||||
}
|
||||
|
||||
@Field(() => Int)
|
||||
id: number
|
||||
|
||||
@Field(() => Int)
|
||||
userId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Boolean)
|
||||
gmsPublishEmail: boolean
|
||||
|
||||
@Field(() => Boolean)
|
||||
emailChecked: boolean
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
countryCode: string | null
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
phone: string | null
|
||||
|
||||
@Field(() => Int)
|
||||
gmsPublishPhone: number
|
||||
|
||||
@Field(() => Date)
|
||||
createdAt: Date
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
updatedAt: Date | null
|
||||
|
||||
@Field(() => Date, { nullable: true })
|
||||
deletedAt: Date | null
|
||||
}
|
||||
@ -2782,13 +2782,15 @@ describe('ContributionResolver', () => {
|
||||
const { errors: errorObjects } = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
statusFilter: ['INVALID_STATUS'],
|
||||
filter: {
|
||||
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.',
|
||||
'Variable "$filter" got invalid value "INVALID_STATUS" at "filter.statusFilter[0]"; Value "INVALID_STATUS" does not exist in "ContributionStatus" enum.',
|
||||
extensions: {
|
||||
code: 'BAD_USER_INPUT',
|
||||
},
|
||||
@ -2801,6 +2803,7 @@ describe('ContributionResolver', () => {
|
||||
data: { adminListContributions: contributionListObject },
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: { paginated: { pageSize: 20 } },
|
||||
})
|
||||
|
||||
expect(contributionListObject.contributionList).toHaveLength(18)
|
||||
@ -2809,165 +2812,255 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: '#firefighters',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(50),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Herzlich Willkommen bei Gradido liebe Bibi!',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(50),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Herzlich Willkommen bei Gradido liebe Bibi!',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(450),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Herzlich Willkommen bei Gradido liebe Bibi!',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(400),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Bob',
|
||||
id: expect.any(Number),
|
||||
lastName: 'der Baumeister',
|
||||
memo: 'Confirmed Contribution',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bob',
|
||||
lastName: 'der Baumeister',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bob@baumeister.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Test env contribution',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(200),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Aktives Grundeinkommen',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(200),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Das war leider zu Viel!',
|
||||
messagesCount: 1,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'DENIED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Test contribution to delete',
|
||||
messagesCount: 0,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Test contribution to deny',
|
||||
messagesCount: 0,
|
||||
status: 'DENIED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Test contribution to confirm',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Test IN_PROGRESS contribution',
|
||||
messagesCount: 1,
|
||||
status: 'IN_PROGRESS',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(10),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Test PENDING contribution update',
|
||||
messagesCount: 2,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(1000),
|
||||
firstName: 'Bibi',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Bloxberg',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'bibi@bloxberg.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
@ -2979,10 +3072,14 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
currentPage: 1,
|
||||
pageSize: 2,
|
||||
order: Order.DESC,
|
||||
statusFilter: ['PENDING'],
|
||||
paginated: {
|
||||
currentPage: 1,
|
||||
pageSize: 2,
|
||||
order: Order.DESC,
|
||||
},
|
||||
filter: {
|
||||
statusFilter: ['PENDING'],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(2)
|
||||
@ -2991,21 +3088,31 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: '100',
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: '#firefighters',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: '400',
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.not.objectContaining({
|
||||
status: 'DENIED',
|
||||
@ -3030,7 +3137,10 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
query: 'Peter',
|
||||
filter: {
|
||||
query: 'Peter',
|
||||
},
|
||||
paginated: { pageSize: 20 },
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(4)
|
||||
@ -3039,39 +3149,59 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: '#firefighters',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(400),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Test env contribution',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(200),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Das war leider zu Viel!',
|
||||
messagesCount: 1,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
@ -3083,8 +3213,11 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
query: 'Peter',
|
||||
noHashtag: true,
|
||||
filter: {
|
||||
query: 'Peter',
|
||||
noHashtag: true,
|
||||
},
|
||||
paginated: { pageSize: 20 },
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(3)
|
||||
@ -3093,30 +3226,45 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(400),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Test env contribution',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(200),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: 'Das war leider zu Viel!',
|
||||
messagesCount: 1,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
@ -3128,7 +3276,9 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
query: '#firefighter',
|
||||
filter: {
|
||||
query: '#firefighter',
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(1)
|
||||
@ -3137,12 +3287,17 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(100),
|
||||
firstName: 'Peter',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Lustig',
|
||||
memo: '#firefighters',
|
||||
messagesCount: 0,
|
||||
status: 'PENDING',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'peter@lustig.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
@ -3154,8 +3309,10 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
query: '#firefighter',
|
||||
noHashtag: true,
|
||||
filter: {
|
||||
query: '#firefighter',
|
||||
noHashtag: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(0)
|
||||
@ -3171,7 +3328,10 @@ describe('ContributionResolver', () => {
|
||||
} = await query({
|
||||
query: adminListContributions,
|
||||
variables: {
|
||||
query: 'RAEUBER', // only found in lowercase in the email
|
||||
filter: {
|
||||
query: 'RAEUBER', // only found in lowercase in the email
|
||||
},
|
||||
paginated: { pageSize: 20 },
|
||||
},
|
||||
})
|
||||
expect(contributionListObject.contributionList).toHaveLength(3)
|
||||
@ -3180,30 +3340,45 @@ describe('ContributionResolver', () => {
|
||||
contributionList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'DENIED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'DELETED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
amount: expect.decimalEqual(166),
|
||||
firstName: 'Räuber',
|
||||
id: expect.any(Number),
|
||||
lastName: 'Hotzenplotz',
|
||||
memo: 'Whatever contribution',
|
||||
messagesCount: 0,
|
||||
status: 'CONFIRMED',
|
||||
user: expect.objectContaining({
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
emailContact: expect.objectContaining({
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
|
||||
@ -4,7 +4,20 @@ import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { UserContact } from '@entity/UserContact'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import {
|
||||
Arg,
|
||||
Args,
|
||||
Authorized,
|
||||
Ctx,
|
||||
FieldResolver,
|
||||
Info,
|
||||
Int,
|
||||
Mutation,
|
||||
Query,
|
||||
Resolver,
|
||||
Root,
|
||||
} from 'type-graphql'
|
||||
|
||||
import { AdminCreateContributionArgs } from '@arg/AdminCreateContributionArgs'
|
||||
import { AdminUpdateContributionArgs } from '@arg/AdminUpdateContributionArgs'
|
||||
@ -20,6 +33,7 @@ import { Contribution, ContributionListResult } from '@model/Contribution'
|
||||
import { Decay } from '@model/Decay'
|
||||
import { OpenCreation } from '@model/OpenCreation'
|
||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||
import { User } from '@model/User'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import {
|
||||
@ -48,11 +62,12 @@ import { fullName } from '@/util/utilities'
|
||||
|
||||
import { findContribution } from './util/contributions'
|
||||
import { getUserCreation, validateContribution, getOpenCreations } from './util/creations'
|
||||
import { extractGraphQLFields, extractGraphQLFieldsForSelect } from './util/extractGraphQLFields'
|
||||
import { findContributions } from './util/findContributions'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
|
||||
@Resolver()
|
||||
@Resolver(() => Contribution)
|
||||
export class ContributionResolver {
|
||||
@Authorized([RIGHTS.ADMIN_LIST_CONTRIBUTIONS])
|
||||
@Query(() => Contribution)
|
||||
@ -321,15 +336,37 @@ export class ContributionResolver {
|
||||
@Authorized([RIGHTS.ADMIN_LIST_CONTRIBUTIONS])
|
||||
@Query(() => ContributionListResult)
|
||||
async adminListContributions(
|
||||
@Args() paginated: Paginated,
|
||||
@Args() filter: SearchContributionsFilterArgs,
|
||||
): Promise<ContributionListResult> {
|
||||
const [dbContributions, count] = await findContributions(paginated, filter, true, {
|
||||
user: {
|
||||
emailContact: true,
|
||||
},
|
||||
messages: true,
|
||||
@Arg('filter', () => SearchContributionsFilterArgs, {
|
||||
defaultValue: new SearchContributionsFilterArgs(),
|
||||
})
|
||||
filter: SearchContributionsFilterArgs,
|
||||
@Arg('paginated', () => Paginated, { defaultValue: new Paginated() }) paginated: Paginated,
|
||||
@Info() info: GraphQLResolveInfo,
|
||||
): Promise<ContributionListResult> {
|
||||
// Check if only count was requested (without contributionList)
|
||||
const fields = Object.keys(extractGraphQLFields(info))
|
||||
const countOnly: boolean = fields.includes('contributionCount') && fields.length === 1
|
||||
// check if related user was requested
|
||||
const userRequested =
|
||||
fields.includes('user') || filter.userId !== undefined || filter.query !== undefined
|
||||
// check if related emailContact was requested
|
||||
const emailContactRequested = fields.includes('user.emailContact') || filter.query !== undefined
|
||||
// check if related messages were requested
|
||||
const messagesRequested = ['messagesCount', 'messages'].some((field) => fields.includes(field))
|
||||
const [dbContributions, count] = await findContributions(
|
||||
paginated,
|
||||
filter,
|
||||
true,
|
||||
{
|
||||
user: userRequested
|
||||
? {
|
||||
emailContact: emailContactRequested,
|
||||
}
|
||||
: false,
|
||||
messages: messagesRequested,
|
||||
},
|
||||
countOnly,
|
||||
)
|
||||
|
||||
return new ContributionListResult(
|
||||
count,
|
||||
@ -573,4 +610,21 @@ export class ContributionResolver {
|
||||
|
||||
return !!res
|
||||
}
|
||||
|
||||
// Field resolvers
|
||||
@Authorized([RIGHTS.USER])
|
||||
@FieldResolver(() => User)
|
||||
async user(
|
||||
@Root() contribution: DbContribution,
|
||||
@Info() info: GraphQLResolveInfo,
|
||||
): Promise<User> {
|
||||
let user = contribution.user
|
||||
if (!user) {
|
||||
const queryBuilder = DbUser.createQueryBuilder('user')
|
||||
queryBuilder.where('user.id = :userId', { userId: contribution.userId })
|
||||
extractGraphQLFieldsForSelect(info, queryBuilder, 'user')
|
||||
user = await queryBuilder.getOneOrFail()
|
||||
}
|
||||
return new User(user)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,21 @@ import { ProjectBranding } from '@entity/ProjectBranding'
|
||||
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { UserContact as DbUserContact } from '@entity/UserContact'
|
||||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import i18n from 'i18n'
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, Mutation, Int } from 'type-graphql'
|
||||
import {
|
||||
Resolver,
|
||||
Query,
|
||||
Args,
|
||||
Arg,
|
||||
Authorized,
|
||||
Ctx,
|
||||
Mutation,
|
||||
Int,
|
||||
Root,
|
||||
FieldResolver,
|
||||
Info,
|
||||
} from 'type-graphql'
|
||||
import { IRestResponse } from 'typed-rest-client'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
@ -29,6 +42,7 @@ import { SearchAdminUsersResult } from '@model/AdminUser'
|
||||
import { GmsUserAuthenticationResult } from '@model/GmsUserAuthenticationResult'
|
||||
import { User } from '@model/User'
|
||||
import { UserAdmin, SearchUsersResult } from '@model/UserAdmin'
|
||||
import { UserContact } from '@model/UserContact'
|
||||
import { UserLocationResult } from '@model/UserLocationResult'
|
||||
|
||||
import { HumHubClient } from '@/apis/humhub/HumHubClient'
|
||||
@ -79,6 +93,7 @@ import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlaygro
|
||||
import { getHomeCommunity } from './util/communities'
|
||||
import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings'
|
||||
import { getUserCreations } from './util/creations'
|
||||
import { extractGraphQLFieldsForSelect } from './util/extractGraphQLFields'
|
||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||
import { findUsers } from './util/findUsers'
|
||||
import { getKlicktippState } from './util/getKlicktippState'
|
||||
@ -126,7 +141,7 @@ const newGradidoID = async (): Promise<string> => {
|
||||
return gradidoId
|
||||
}
|
||||
|
||||
@Resolver()
|
||||
@Resolver(() => User)
|
||||
export class UserResolver {
|
||||
@Authorized([RIGHTS.VERIFY_LOGIN])
|
||||
@Query(() => User)
|
||||
@ -1035,6 +1050,30 @@ export class UserResolver {
|
||||
const modelUser = new User(foundDbUser)
|
||||
return modelUser
|
||||
}
|
||||
|
||||
// FIELD RESOLVERS
|
||||
@FieldResolver(() => UserContact)
|
||||
async emailContact(
|
||||
@Root() user: DbUser,
|
||||
@Ctx() context: Context,
|
||||
@Info() info: GraphQLResolveInfo,
|
||||
): Promise<UserContact> {
|
||||
// Check if user has the necessary permissions to view user contact
|
||||
// Either they need VIEW_USER_CONTACT right, or they need VIEW_OWN_USER_CONTACT and must be viewing their own contact
|
||||
if (!context.role?.hasRight(RIGHTS.VIEW_USER_CONTACT)) {
|
||||
if (!context.role?.hasRight(RIGHTS.VIEW_OWN_USER_CONTACT) || context.user?.id !== user.id) {
|
||||
throw new LogError('User does not have permission to view this user contact', user.id)
|
||||
}
|
||||
}
|
||||
let userContact = user.emailContact
|
||||
if (!userContact) {
|
||||
const queryBuilder = DbUserContact.createQueryBuilder('userContact')
|
||||
queryBuilder.where('userContact.userId = :userId', { userId: user.id })
|
||||
extractGraphQLFieldsForSelect(info, queryBuilder, 'userContact')
|
||||
userContact = await queryBuilder.getOneOrFail()
|
||||
}
|
||||
return new UserContact(userContact)
|
||||
}
|
||||
}
|
||||
|
||||
export async function findUserByEmail(email: string): Promise<DbUser> {
|
||||
|
||||
48
backend/src/graphql/resolver/util/extractGraphQLFields.ts
Normal file
48
backend/src/graphql/resolver/util/extractGraphQLFields.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { ObjectLiteral, SelectQueryBuilder } from '@dbTools/typeorm'
|
||||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import {
|
||||
parseResolveInfo,
|
||||
ResolveTree,
|
||||
simplifyParsedResolveInfoFragmentWithType,
|
||||
} from 'graphql-parse-resolve-info'
|
||||
|
||||
/**
|
||||
* Extracts the requested fields from GraphQL
|
||||
* @param info GraphQLResolveInfo
|
||||
*/
|
||||
export function extractGraphQLFields(info: GraphQLResolveInfo): object {
|
||||
const parsedInfo = parseResolveInfo(info)
|
||||
if (!parsedInfo) {
|
||||
throw new Error('Could not parse resolve info')
|
||||
}
|
||||
|
||||
return simplifyParsedResolveInfoFragmentWithType(parsedInfo as ResolveTree, info.returnType)
|
||||
.fields
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the requested fields from GraphQL and applies them to a TypeORM query.
|
||||
* @param info GraphQLResolveInfo
|
||||
* @param queryBuilder TypeORM QueryBuilder
|
||||
* @param alias the table alias for select
|
||||
*/
|
||||
export function extractGraphQLFieldsForSelect<T extends ObjectLiteral>(
|
||||
info: GraphQLResolveInfo,
|
||||
queryBuilder: SelectQueryBuilder<T>,
|
||||
alias: string,
|
||||
) {
|
||||
const requestedFields = Object.keys(extractGraphQLFields(info))
|
||||
|
||||
if (requestedFields.length > 0) {
|
||||
// Filter out fields that don't exist in the entity type T
|
||||
const entityName = queryBuilder.alias.charAt(0).toUpperCase() + queryBuilder.alias.slice(1)
|
||||
const metadata = queryBuilder.connection.getMetadata(entityName)
|
||||
const validFields = requestedFields.filter(
|
||||
(field) => metadata.findColumnWithPropertyName(field) !== undefined,
|
||||
)
|
||||
|
||||
if (requestedFields.length > 0) {
|
||||
queryBuilder.select(validFields.map((field) => `${alias}.${field}`))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,6 @@ import { Paginated } from '@arg/Paginated'
|
||||
import { SearchContributionsFilterArgs } from '@arg/SearchContributionsFilterArgs'
|
||||
import { Connection } from '@typeorm/connection'
|
||||
|
||||
import { Order } from '@/graphql/enum/Order'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
interface Relations {
|
||||
@ -36,10 +35,11 @@ function joinRelationsRecursive(
|
||||
}
|
||||
|
||||
export const findContributions = async (
|
||||
{ pageSize = 3, currentPage = 1, order = Order.DESC }: Paginated,
|
||||
{ pageSize, currentPage, order }: Paginated,
|
||||
filter: SearchContributionsFilterArgs,
|
||||
withDeleted = false,
|
||||
relations: Relations | undefined = undefined,
|
||||
countOnly = false,
|
||||
): Promise<[DbContribution[], number]> => {
|
||||
const connection = await Connection.getInstance()
|
||||
if (!connection) {
|
||||
@ -76,6 +76,9 @@ export const findContributions = async (
|
||||
}),
|
||||
)
|
||||
}
|
||||
if (countOnly) {
|
||||
return [[], await queryBuilder.getCount()]
|
||||
}
|
||||
return queryBuilder
|
||||
.orderBy('Contribution.createdAt', order)
|
||||
.addOrderBy('Contribution.id', order)
|
||||
|
||||
@ -300,39 +300,39 @@ query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusF
|
||||
// from admin interface
|
||||
|
||||
export const adminListContributions = gql`
|
||||
query (
|
||||
$currentPage: Int = 1
|
||||
$pageSize: Int = 25
|
||||
$order: Order = DESC
|
||||
$statusFilter: [ContributionStatus!]
|
||||
$userId: Int
|
||||
$query: String
|
||||
$noHashtag: Boolean
|
||||
) {
|
||||
adminListContributions(
|
||||
currentPage: $currentPage
|
||||
pageSize: $pageSize
|
||||
order: $order
|
||||
statusFilter: $statusFilter
|
||||
userId: $userId
|
||||
query: $query
|
||||
noHashtag: $noHashtag
|
||||
) {
|
||||
query ($filter: SearchContributionsFilterArgs, $paginated: Paginated) {
|
||||
adminListContributions(filter: $filter, paginated: $paginated) {
|
||||
contributionCount
|
||||
contributionList {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
user {
|
||||
emailContact {
|
||||
email
|
||||
}
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
alias
|
||||
humhubUsername
|
||||
createdAt
|
||||
}
|
||||
amount
|
||||
memo
|
||||
createdAt
|
||||
contributionDate
|
||||
confirmedAt
|
||||
confirmedBy
|
||||
contributionDate
|
||||
updatedAt
|
||||
updatedBy
|
||||
status
|
||||
messagesCount
|
||||
deniedAt
|
||||
deniedBy
|
||||
deletedAt
|
||||
deletedBy
|
||||
moderatorId
|
||||
userId
|
||||
resubmissionAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3852,6 +3852,14 @@ graphql-extensions@^0.16.0:
|
||||
apollo-server-env "^3.2.0"
|
||||
apollo-server-types "^0.10.0"
|
||||
|
||||
graphql-parse-resolve-info@^4.13.0:
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.13.0.tgz#03627032e25917bd6f9ed89e768568c61200e6ff"
|
||||
integrity sha512-VVJ1DdHYcR7hwOGQKNH+QTzuNgsLA8l/y436HtP9YHoX6nmwXRWq3xWthU3autMysXdm0fQUbhTZCx0W9ICozw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
tslib "^2.0.1"
|
||||
|
||||
graphql-query-complexity@^0.7.0:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/graphql-query-complexity/-/graphql-query-complexity-0.7.2.tgz#7fc6bb20930ab1b666ecf3bbfb24b65b6f08ecc4"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user