Merge pull request #1914 from gradido/1840-refactor-to-filter-object--sub-rename+filters-obj

refactor: 🍰 Refactor To `filters` Object And Rename Filters Properties
This commit is contained in:
Wolfgang Huß 2022-06-02 11:44:54 +02:00 committed by GitHub
commit 7bf026f704
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 411 additions and 108 deletions

View File

@ -528,7 +528,7 @@ jobs:
report_name: Coverage Backend
type: lcov
result_path: ./backend/coverage/lcov.info
min_coverage: 66
min_coverage: 68
token: ${{ github.token }}
##########################################################################

View File

@ -2,7 +2,12 @@ import gql from 'graphql-tag'
export const listTransactionLinksAdmin = gql`
query ($currentPage: Int = 1, $pageSize: Int = 5, $userId: Int!) {
listTransactionLinksAdmin(currentPage: $currentPage, pageSize: $pageSize, userId: $userId) {
listTransactionLinksAdmin(
currentPage: $currentPage
pageSize: $pageSize
userId: $userId
filters: { withRedeemed: true, withExpired: true, withDeleted: true }
) {
linkCount
linkList {
id

View File

@ -1,12 +1,7 @@
import gql from 'graphql-tag'
export const searchUsers = gql`
query (
$searchText: String!
$currentPage: Int
$pageSize: Int
$filters: SearchUsersFiltersInput
) {
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) {
searchUsers(
searchText: $searchText
currentPage: $currentPage

View File

@ -72,8 +72,8 @@ describe('Creation', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: true,
filterByDeleted: false,
byActivated: true,
byDeleted: false,
},
},
}),
@ -274,8 +274,8 @@ describe('Creation', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: true,
filterByDeleted: false,
byActivated: true,
byDeleted: false,
},
},
}),
@ -293,8 +293,8 @@ describe('Creation', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: true,
filterByDeleted: false,
byActivated: true,
byDeleted: false,
},
},
}),
@ -312,8 +312,8 @@ describe('Creation', () => {
currentPage: 2,
pageSize: 25,
filters: {
filterByActivated: true,
filterByDeleted: false,
byActivated: true,
byDeleted: false,
},
},
}),

View File

@ -103,8 +103,8 @@ export default {
currentPage: this.currentPage,
pageSize: this.perPage,
filters: {
filterByActivated: true,
filterByDeleted: false,
byActivated: true,
byDeleted: false,
},
},
fetchPolicy: 'network-only',

View File

@ -83,8 +83,8 @@ describe('UserSearch', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: null,
filterByDeleted: null,
byActivated: null,
byDeleted: null,
},
},
}),
@ -104,8 +104,8 @@ describe('UserSearch', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: false,
filterByDeleted: null,
byActivated: false,
byDeleted: null,
},
},
}),
@ -126,8 +126,8 @@ describe('UserSearch', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: null,
filterByDeleted: true,
byActivated: null,
byDeleted: true,
},
},
}),
@ -148,8 +148,8 @@ describe('UserSearch', () => {
currentPage: 2,
pageSize: 25,
filters: {
filterByActivated: null,
filterByDeleted: null,
byActivated: null,
byDeleted: null,
},
},
}),
@ -170,8 +170,8 @@ describe('UserSearch', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: null,
filterByDeleted: null,
byActivated: null,
byDeleted: null,
},
},
}),
@ -189,8 +189,8 @@ describe('UserSearch', () => {
currentPage: 1,
pageSize: 25,
filters: {
filterByActivated: null,
filterByDeleted: null,
byActivated: null,
byDeleted: null,
},
},
}),

View File

@ -4,9 +4,9 @@
<b-button class="unconfirmedRegisterMails" variant="light" @click="unconfirmedRegisterMails">
<b-icon icon="envelope" variant="danger"></b-icon>
{{
filterByActivated === null
filters.byActivated === null
? $t('all_emails')
: filterByActivated === false
: filters.byActivated === false
? $t('unregistered_emails')
: ''
}}
@ -14,9 +14,9 @@
<b-button class="deletedUserSearch" variant="light" @click="deletedUserSearch">
<b-icon icon="x-circle" variant="danger"></b-icon>
{{
filterByDeleted === null
filters.byDeleted === null
? $t('all_emails')
: filterByDeleted === true
: filters.byDeleted === true
? $t('deleted_user')
: ''
}}
@ -72,8 +72,10 @@ export default {
searchResult: [],
massCreation: [],
criteria: '',
filterByActivated: null,
filterByDeleted: null,
filters: {
byActivated: null,
byDeleted: null,
},
rows: 0,
currentPage: 1,
perPage: 25,
@ -82,11 +84,11 @@ export default {
},
methods: {
unconfirmedRegisterMails() {
this.filterByActivated = this.filterByActivated === null ? false : null
this.filters.byActivated = this.filters.byActivated === null ? false : null
this.getUsers()
},
deletedUserSearch() {
this.filterByDeleted = this.filterByDeleted === null ? true : null
this.filters.byDeleted = this.filters.byDeleted === null ? true : null
this.getUsers()
},
getUsers() {
@ -97,10 +99,7 @@ export default {
searchText: this.criteria,
currentPage: this.currentPage,
pageSize: this.perPage,
filters: {
filterByActivated: this.filterByActivated,
filterByDeleted: this.filterByDeleted,
},
filters: this.filters,
},
fetchPolicy: 'no-cache',
})

View File

@ -12,6 +12,6 @@ export default class SearchUsersArgs {
@Field(() => Int, { nullable: true })
pageSize?: number
@Field(() => SearchUsersFilters, { nullable: true })
@Field(() => SearchUsersFilters, { nullable: true, defaultValue: null })
filters: SearchUsersFilters
}

View File

@ -1,11 +1,10 @@
import { Field, InputType, ObjectType } from 'type-graphql'
import { Field, InputType } from 'type-graphql'
@ObjectType()
@InputType('SearchUsersFiltersInput')
@InputType()
export default class SearchUsersFilters {
@Field(() => Boolean, { nullable: true, defaultValue: null })
filterByActivated?: boolean | null
byActivated: boolean
@Field(() => Boolean, { nullable: true, defaultValue: null })
filterByDeleted?: boolean | null
byDeleted: boolean
}

View File

@ -1,13 +1,13 @@
import { ArgsType, Field } from 'type-graphql'
import { Field, InputType } from 'type-graphql'
@ArgsType()
@InputType()
export default class TransactionLinkFilters {
@Field(() => Boolean, { nullable: true, defaultValue: true })
filterByDeleted?: boolean
@Field(() => Boolean, { nullable: true })
withDeleted: boolean
@Field(() => Boolean, { nullable: true, defaultValue: true })
filterByExpired?: boolean
@Field(() => Boolean, { nullable: true })
withExpired: boolean
@Field(() => Boolean, { nullable: true, defaultValue: true })
filterByRedeemed?: boolean
@Field(() => Boolean, { nullable: true })
withRedeemed: boolean
}

View File

@ -1,10 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { convertObjValuesToArray } from '@/util/utilities'
import { objectValuesToArray } from '@/util/utilities'
import { testEnvironment, resetToken, cleanDB } from '@test/helpers'
import { userFactory } from '@/seeds/factory/user'
import { creationFactory } from '@/seeds/factory/creation'
import { creations } from '@/seeds/creation/index'
import { transactionLinkFactory } from '@/seeds/factory/transactionLink'
import { transactionLinks } from '@/seeds/transactionLink/index'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig'
import { stephenHawking } from '@/seeds/users/stephen-hawking'
@ -12,14 +15,18 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
import {
deleteUser,
unDeleteUser,
searchUsers,
createPendingCreation,
createPendingCreations,
updatePendingCreation,
deletePendingCreation,
confirmPendingCreation,
} from '@/seeds/graphql/mutations'
import { getPendingCreations, login } from '@/seeds/graphql/queries'
import {
getPendingCreations,
login,
searchUsers,
listTransactionLinksAdmin,
} from '@/seeds/graphql/queries'
import { GraphQLError } from 'graphql'
import { User } from '@entity/User'
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
@ -366,7 +373,7 @@ describe('AdminResolver', () => {
data: {
searchUsers: {
userCount: 4,
userList: expect.arrayContaining(convertObjValuesToArray(allUsers)),
userList: expect.arrayContaining(objectValuesToArray(allUsers)),
},
},
}),
@ -382,8 +389,8 @@ describe('AdminResolver', () => {
variables: {
...variablesWithoutTextAndFilters,
filters: {
filterByActivated: null,
filterByDeleted: null,
byActivated: null,
byDeleted: null,
},
},
}),
@ -392,7 +399,7 @@ describe('AdminResolver', () => {
data: {
searchUsers: {
userCount: 4,
userList: expect.arrayContaining(convertObjValuesToArray(allUsers)),
userList: expect.arrayContaining(objectValuesToArray(allUsers)),
},
},
}),
@ -408,8 +415,8 @@ describe('AdminResolver', () => {
variables: {
...variablesWithoutTextAndFilters,
filters: {
filterByActivated: false,
filterByDeleted: null,
byActivated: false,
byDeleted: null,
},
},
}),
@ -434,8 +441,8 @@ describe('AdminResolver', () => {
variables: {
...variablesWithoutTextAndFilters,
filters: {
filterByActivated: null,
filterByDeleted: true,
byActivated: null,
byDeleted: true,
},
},
}),
@ -460,8 +467,8 @@ describe('AdminResolver', () => {
variables: {
...variablesWithoutTextAndFilters,
filters: {
filterByActivated: false,
filterByDeleted: true,
byActivated: false,
byDeleted: true,
},
},
}),
@ -1324,4 +1331,266 @@ describe('AdminResolver', () => {
})
})
})
describe('transaction links list', () => {
const variables = {
userId: 1, // dummy, may be replaced
filters: null,
currentPage: 1,
pageSize: 5,
}
describe('unauthenticated', () => {
it('returns an error', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables,
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('401 Unauthorized')],
}),
)
})
})
describe('authenticated', () => {
describe('without admin rights', () => {
beforeAll(async () => {
user = await userFactory(testEnv, bibiBloxberg)
await query({
query: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
})
afterAll(async () => {
await cleanDB()
resetToken()
})
it('returns an error', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables,
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('401 Unauthorized')],
}),
)
})
})
describe('with admin rights', () => {
beforeAll(async () => {
// admin 'peter@lustig.de' has to exists for 'creationFactory'
admin = await userFactory(testEnv, peterLustig)
user = await userFactory(testEnv, bibiBloxberg)
variables.userId = user.id
variables.pageSize = 25
// bibi needs GDDs
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await creationFactory(testEnv, bibisCreation!)
// bibis transaktion links
const bibisTransaktionLinks = transactionLinks.filter(
(transactionLink) => transactionLink.email === 'bibi@bloxberg.de',
)
for (let i = 0; i < bibisTransaktionLinks.length; i++) {
await transactionLinkFactory(testEnv, bibisTransaktionLinks[i])
}
// admin: only now log in
await query({
query: login,
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
})
})
afterAll(async () => {
await cleanDB()
resetToken()
})
describe('without any filters', () => {
it('finds 6 open transaction links and no deleted or redeemed', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables,
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listTransactionLinksAdmin: {
linkCount: 6,
linkList: expect.not.arrayContaining([
expect.objectContaining({
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
createdAt: expect.any(String),
}),
expect.objectContaining({
memo: 'Da habe ich mich wohl etwas übernommen.',
deletedAt: expect.any(String),
}),
]),
},
},
}),
)
})
})
describe('all filters are null', () => {
it('finds 6 open transaction links and no deleted or redeemed', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables: {
...variables,
filters: {
withDeleted: null,
withExpired: null,
withRedeemed: null,
},
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listTransactionLinksAdmin: {
linkCount: 6,
linkList: expect.not.arrayContaining([
expect.objectContaining({
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
createdAt: expect.any(String),
}),
expect.objectContaining({
memo: 'Da habe ich mich wohl etwas übernommen.',
deletedAt: expect.any(String),
}),
]),
},
},
}),
)
})
})
describe('filter with deleted', () => {
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables: {
...variables,
filters: {
withDeleted: true,
},
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listTransactionLinksAdmin: {
linkCount: 7,
linkList: expect.arrayContaining([
expect.not.objectContaining({
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
createdAt: expect.any(String),
}),
expect.objectContaining({
memo: 'Da habe ich mich wohl etwas übernommen.',
deletedAt: expect.any(String),
}),
]),
},
},
}),
)
})
})
describe('filter by expired', () => {
it('finds 5 open transaction links, 1 expired, and no redeemed', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables: {
...variables,
filters: {
withExpired: true,
},
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listTransactionLinksAdmin: {
linkCount: 7,
linkList: expect.arrayContaining([
expect.objectContaining({
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
createdAt: expect.any(String),
}),
expect.not.objectContaining({
memo: 'Da habe ich mich wohl etwas übernommen.',
deletedAt: expect.any(String),
}),
]),
},
},
}),
)
})
})
// TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory
describe.skip('filter by redeemed', () => {
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
await expect(
query({
query: listTransactionLinksAdmin,
variables: {
...variables,
filters: {
withDeleted: null,
withExpired: null,
withRedeemed: true,
},
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listTransactionLinksAdmin: {
linkCount: 6,
linkList: expect.arrayContaining([
expect.not.objectContaining({
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
createdAt: expect.any(String),
}),
expect.objectContaining({
memo: 'Yeah, eingelöst!',
redeemedAt: expect.any(String),
redeemedBy: expect.any(Number),
}),
expect.not.objectContaining({
memo: 'Da habe ich mich wohl etwas übernommen.',
deletedAt: expect.any(String),
}),
]),
},
},
}),
)
})
})
})
})
})
})

View File

@ -58,12 +58,12 @@ export class AdminResolver {
const filterCriteria: ObjectLiteral[] = []
if (filters) {
if (filters.filterByActivated !== null) {
filterCriteria.push({ emailChecked: filters.filterByActivated })
if (filters.byActivated !== null) {
filterCriteria.push({ emailChecked: filters.byActivated })
}
if (filters.filterByDeleted !== null) {
filterCriteria.push({ deletedAt: filters.filterByDeleted ? Not(IsNull()) : IsNull() })
if (filters.byDeleted !== null) {
filterCriteria.push({ deletedAt: filters.byDeleted ? Not(IsNull()) : IsNull() })
}
}
@ -426,9 +426,10 @@ export class AdminResolver {
async listTransactionLinksAdmin(
@Args()
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
@Args()
@Arg('filters', () => TransactionLinkFilters, { nullable: true })
filters: TransactionLinkFilters,
@Arg('userId', () => Int) userId: number,
@Arg('userId', () => Int)
userId: number,
): Promise<TransactionLinkResult> {
const user = await dbUser.findOneOrFail({ id: userId })
const where: {
@ -437,12 +438,16 @@ export class AdminResolver {
validUntil?: FindOperator<Date> | null
} = {
userId,
redeemedBy: null,
validUntil: MoreThan(new Date()),
}
if (filters) {
if (filters.withRedeemed) delete where.redeemedBy
if (filters.withExpired) delete where.validUntil
}
if (!filters.filterByRedeemed) where.redeemedBy = null
if (!filters.filterByExpired) where.validUntil = MoreThan(new Date())
const [transactionLinks, count] = await dbTransactionLink.findAndCount({
where,
withDeleted: filters.filterByDeleted,
withDeleted: filters ? filters.withDeleted : false,
order: {
createdAt: order,
},

View File

@ -105,35 +105,6 @@ export const unDeleteUser = gql`
}
`
export const searchUsers = gql`
query (
$searchText: String!
$currentPage: Int
$pageSize: Int
$filters: SearchUsersFiltersInput
) {
searchUsers(
searchText: $searchText
currentPage: $currentPage
pageSize: $pageSize
filters: $filters
) {
userCount
userList {
userId
firstName
lastName
email
creation
emailChecked
hasElopage
emailConfirmationSend
deletedAt
}
}
}
`
export const createPendingCreations = gql`
mutation ($pendingCreations: [CreatePendingCreationArgs!]!) {
createPendingCreations(pendingCreations: $pendingCreations) {

View File

@ -91,6 +91,30 @@ export const sendResetPasswordEmail = gql`
}
`
export const searchUsers = gql`
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) {
searchUsers(
searchText: $searchText
currentPage: $currentPage
pageSize: $pageSize
filters: $filters
) {
userCount
userList {
userId
firstName
lastName
email
creation
emailChecked
hasElopage
emailConfirmationSend
deletedAt
}
}
}
`
export const listGDTEntriesQuery = gql`
query ($currentPage: Int!, $pageSize: Int!) {
listGDTEntries(currentPage: $currentPage, pageSize: $pageSize) {
@ -164,3 +188,32 @@ export const getPendingCreations = gql`
}
}
`
export const listTransactionLinksAdmin = gql`
query (
$userId: Int!
$filters: TransactionLinkFilters
$currentPage: Int = 1
$pageSize: Int = 5
) {
listTransactionLinksAdmin(
userId: $userId
filters: $filters
currentPage: $currentPage
pageSize: $pageSize
) {
linkCount
linkList {
id
amount
holdAvailableAmount
memo
code
createdAt
validUntil
redeemedAt
deletedAt
}
}
}
`

View File

@ -3,5 +3,8 @@ export interface TransactionLinkInterface {
amount: number
memo: string
createdAt?: Date
// TODO: for testing
// redeemedAt?: Date
// redeemedBy?: number
deletedAt?: boolean
}

View File

@ -30,6 +30,10 @@ bei Gradidio sei dabei!`,
amount: 19.99,
memo: `Kein Trick, keine Zauberrei,
bei Gradidio sei dabei!`,
// TODO: for testing
// memo: `Yeah, eingelöst!`,
// redeemedAt: new Date(2022, 2, 2),
// redeemedBy: not null,
},
{
email: 'bibi@bloxberg.de',

View File

@ -1,4 +1,4 @@
export const convertObjValuesToArray = (obj: { [x: string]: string }): Array<string> => {
export const objectValuesToArray = (obj: { [x: string]: string }): Array<string> => {
return Object.keys(obj).map(function (key) {
return obj[key]
})