Merge branch 'master' into federation-fix-database-public-key-length

This commit is contained in:
Ulf Gebhardt 2023-06-30 15:44:01 +02:00 committed by GitHub
commit 4a9568a99d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 237 additions and 28 deletions

View File

@ -0,0 +1,37 @@
<template>
<div>
<b-input-group>
<b-form-input
type="text"
class="test-input-criteria"
v-model="currentValue"
:placeholder="$t('user_search')"
></b-form-input>
<b-input-group-append class="test-click-clear-criteria" @click="currentValue = ''">
<b-input-group-text class="pointer">
<b-icon icon="x" />
</b-input-group-text>
</b-input-group-append>
</b-input-group>
</div>
</template>
<script>
export default {
name: 'UserQuery',
props: {
value: { type: String, default: '' },
},
data() {
return {
currentValue: this.value,
}
},
watch: {
currentValue() {
if (this.value !== this.currentValue) {
this.$emit('input', this.currentValue)
}
},
},
}
</script>

View File

@ -7,6 +7,7 @@ export const adminListContributions = gql`
$order: Order = DESC
$statusFilter: [ContributionStatus!]
$userId: Int
$query: String
) {
adminListContributions(
currentPage: $currentPage
@ -14,6 +15,7 @@ export const adminListContributions = gql`
order: $order
statusFilter: $statusFilter
userId: $userId
query: $query
) {
contributionCount
contributionList {

View File

@ -341,6 +341,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['CONFIRMED'],
})
})
@ -356,6 +357,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['IN_PROGRESS', 'PENDING'],
})
})
@ -372,6 +374,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['DENIED'],
})
})
@ -388,6 +391,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['DELETED'],
})
})
@ -404,6 +408,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
})
})
@ -424,6 +429,7 @@ describe('CreationConfirm', () => {
currentPage: 2,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
})
})
@ -439,6 +445,7 @@ describe('CreationConfirm', () => {
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['IN_PROGRESS', 'PENDING'],
})
})
@ -449,6 +456,40 @@ describe('CreationConfirm', () => {
})
})
describe('user query', () => {
describe('with user query', () => {
beforeEach(() => {
wrapper.findComponent({ name: 'UserQuery' }).vm.$emit('input', 'query')
})
it('calls the API with query', () => {
expect(adminListContributionsMock).toBeCalledWith({
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: 'query',
statusFilter: ['IN_PROGRESS', 'PENDING'],
})
})
describe('reset query', () => {
beforeEach(() => {
wrapper.findComponent({ name: 'UserQuery' }).vm.$emit('input', '')
})
it('calls the API with empty query', () => {
expect(adminListContributionsMock).toBeCalledWith({
currentPage: 1,
order: 'DESC',
pageSize: 25,
query: '',
statusFilter: ['IN_PROGRESS', 'PENDING'],
})
})
})
})
})
describe('update status', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('update-status', 2)

View File

@ -1,6 +1,7 @@
<!-- eslint-disable @intlify/vue-i18n/no-dynamic-keys -->
<template>
<div class="creation-confirm">
<user-query class="mb-4 mt-2" v-model="query" />
<div>
<b-tabs v-model="tabIndex" content-class="mt-3" fill>
<b-tab active :title-link-attributes="{ 'data-test': 'open' }">
@ -85,6 +86,7 @@
<script>
import Overlay from '../components/Overlay'
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
import UserQuery from '../components/UserQuery'
import { adminListContributions } from '../graphql/adminListContributions'
import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
import { confirmContribution } from '../graphql/confirmContribution'
@ -103,6 +105,7 @@ export default {
components: {
OpenCreationsTable,
Overlay,
UserQuery,
},
data() {
return {
@ -114,6 +117,7 @@ export default {
rows: 0,
currentPage: 1,
pageSize: 25,
query: '',
}
},
watch: {
@ -409,6 +413,7 @@ export default {
currentPage: this.currentPage,
pageSize: this.pageSize,
statusFilter: this.statusFilter,
query: this.query,
}
},
fetchPolicy: 'no-cache',

View File

@ -186,7 +186,7 @@ describe('UserSearch', () => {
describe('reset the search field', () => {
it('calls the API with empty criteria', async () => {
jest.clearAllMocks()
await wrapper.find('.test-click-clear-criteria').trigger('click')
await wrapper.findComponent({ name: 'UserQuery' }).vm.$emit('input', '')
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {

View File

@ -23,21 +23,7 @@
</b-button>
</div>
<label>{{ $t('user_search') }}</label>
<div>
<b-input-group>
<b-form-input
type="text"
class="test-input-criteria"
v-model="criteria"
:placeholder="$t('user_search')"
></b-form-input>
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
<b-input-group-text class="pointer">
<b-icon icon="x" />
</b-input-group-text>
</b-input-group-append>
</b-input-group>
</div>
<user-query class="mb-4 mt-2" v-model="criteria" />
<search-user-table
type="PageUserSearch"
:items="searchResult"
@ -61,12 +47,14 @@
import SearchUserTable from '../components/Tables/SearchUserTable'
import { searchUsers } from '../graphql/searchUsers'
import { creationMonths } from '../mixins/creationMonths'
import UserQuery from '../components/UserQuery'
export default {
name: 'UserSearch',
mixins: [creationMonths],
components: {
SearchUserTable,
UserQuery,
},
data() {
return {

View File

@ -2890,6 +2890,97 @@ describe('ContributionResolver', () => {
]),
})
})
describe('with user query', () => {
it('returns only contributions of the queried user', async () => {
const {
data: { adminListContributions: contributionListObject },
} = await query({
query: adminListContributions,
variables: {
query: 'Peter',
},
})
expect(contributionListObject.contributionList).toHaveLength(3)
expect(contributionListObject).toMatchObject({
contributionCount: 3,
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',
}),
expect.objectContaining({
amount: expect.decimalEqual(100),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Test env contribution',
messagesCount: 0,
status: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(200),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Das war leider zu Viel!',
messagesCount: 0,
status: 'DELETED',
}),
]),
})
})
// test for case sensitivity and email
it('returns only contributions of the queried user email', async () => {
const {
data: { adminListContributions: contributionListObject },
} = await query({
query: adminListContributions,
variables: {
query: 'RAEUBER', // only found in lowercase in the email
},
})
expect(contributionListObject.contributionList).toHaveLength(3)
expect(contributionListObject).toMatchObject({
contributionCount: 3,
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',
}),
expect.objectContaining({
amount: expect.decimalEqual(166),
firstName: 'Räuber',
id: expect.any(Number),
lastName: 'Hotzenplotz',
memo: 'Whatever contribution',
messagesCount: 0,
status: 'DELETED',
}),
expect.objectContaining({
amount: expect.decimalEqual(166),
firstName: 'Räuber',
id: expect.any(Number),
lastName: 'Hotzenplotz',
memo: 'Whatever contribution',
messagesCount: 0,
status: 'CONFIRMED',
}),
]),
})
})
})
})
})
})

View File

@ -138,7 +138,7 @@ export class ContributionResolver {
currentPage,
pageSize,
withDeleted: true,
relations: ['messages'],
relations: { messages: true },
userId: user.id,
statusFilter,
})
@ -160,7 +160,7 @@ export class ContributionResolver {
order,
currentPage,
pageSize,
relations: ['user'],
relations: { user: true },
statusFilter,
})
@ -372,6 +372,8 @@ export class ContributionResolver {
statusFilter?: ContributionStatus[] | null,
@Arg('userId', () => Int, { nullable: true })
userId?: number | null,
@Arg('query', () => String, { nullable: true })
query?: string | null,
): Promise<ContributionListResult> {
const [dbContributions, count] = await findContributions({
order,
@ -379,8 +381,14 @@ export class ContributionResolver {
pageSize,
withDeleted: true,
userId,
relations: ['user', 'messages'],
relations: {
user: {
emailContact: true,
},
messages: true,
},
statusFilter,
query,
})
return new ContributionListResult(

View File

@ -1,38 +1,73 @@
import { In } from '@dbTools/typeorm'
import { In, Like } from '@dbTools/typeorm'
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionStatus } from '@enum/ContributionStatus'
import { Order } from '@enum/Order'
interface Relations {
[key: string]: boolean | Relations
}
interface FindContributionsOptions {
order: Order
currentPage: number
pageSize: number
withDeleted?: boolean
relations?: string[]
relations?: Relations | undefined
userId?: number | null
statusFilter?: ContributionStatus[] | null
query?: string | null
}
export const findContributions = async (
options: FindContributionsOptions,
): Promise<[DbContribution[], number]> => {
const { order, currentPage, pageSize, withDeleted, relations, userId, statusFilter } = {
const { order, currentPage, pageSize, withDeleted, relations, userId, statusFilter, query } = {
withDeleted: false,
relations: [],
relations: undefined,
query: '',
...options,
}
const requiredWhere = {
...(statusFilter?.length && { contributionStatus: In(statusFilter) }),
...(userId && { userId }),
}
const where =
query && relations?.user
? [
{
...requiredWhere,
user: {
firstName: Like(`%${query}%`),
},
},
{
...requiredWhere,
user: {
lastName: Like(`%${query}%`),
},
},
{
...requiredWhere,
user: {
emailContact: {
email: Like(`%${query}%`),
},
},
},
]
: requiredWhere
return DbContribution.findAndCount({
where: {
...(statusFilter?.length && { contributionStatus: In(statusFilter) }),
...(userId && { userId }),
},
relations,
where,
withDeleted,
order: {
createdAt: order,
id: order,
},
relations,
skip: (currentPage - 1) * pageSize,
take: pageSize,
})

View File

@ -235,6 +235,7 @@ export const adminListContributions = gql`
$order: Order = DESC
$statusFilter: [ContributionStatus!]
$userId: Int
$query: String
) {
adminListContributions(
currentPage: $currentPage
@ -242,6 +243,7 @@ export const adminListContributions = gql`
order: $order
statusFilter: $statusFilter
userId: $userId
query: $query
) {
contributionCount
contributionList {