Merge branch 'master' into eslint-dht-n

This commit is contained in:
clauspeterhuebner 2023-06-16 00:04:44 +02:00 committed by GitHub
commit e71c9085f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 111 deletions

View File

@ -1,12 +1,19 @@
import gql from 'graphql-tag'
export const searchUsers = gql`
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) {
query (
$query: String!
$filters: SearchUsersFilters
$currentPage: Int = 0
$pageSize: Int = 25
$order: Order = ASC
) {
searchUsers(
searchText: $searchText
query: $query
filters: $filters
currentPage: $currentPage
pageSize: $pageSize
filters: $filters
order: $order
) {
userCount
userList {

View File

@ -10,11 +10,20 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
userCount: 4,
userList: [
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
userId: 4,
firstName: 'New',
lastName: 'User',
email: 'new@user.ch',
creation: [1000, 1000, 1000],
emailChecked: false,
deletedAt: null,
},
{
userId: 3,
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
creation: [0, 0, 0],
emailChecked: true,
deletedAt: null,
},
@ -28,23 +37,14 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
deletedAt: new Date(),
},
{
userId: 3,
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
creation: [0, 0, 0],
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
emailChecked: true,
deletedAt: null,
},
{
userId: 4,
firstName: 'New',
lastName: 'User',
email: 'new@user.ch',
creation: [1000, 1000, 1000],
emailChecked: false,
deletedAt: null,
},
],
},
},
@ -79,9 +79,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
query: '',
currentPage: 1,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: null,
byDeleted: null,
@ -100,9 +101,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
query: '',
currentPage: 1,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: false,
byDeleted: null,
@ -122,9 +124,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
query: '',
currentPage: 1,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: null,
byDeleted: true,
@ -144,9 +147,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
query: '',
currentPage: 2,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: null,
byDeleted: null,
@ -166,9 +170,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: 'search string',
query: 'search string',
currentPage: 1,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: null,
byDeleted: null,
@ -185,9 +190,10 @@ describe('UserSearch', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
query: '',
currentPage: 1,
pageSize: 25,
order: 'DESC',
filters: {
byActivated: null,
byDeleted: null,

View File

@ -49,7 +49,7 @@
pills
size="lg"
v-model="currentPage"
per-page="perPage"
:per-page="perPage"
:total-rows="rows"
align="center"
:hide-ellipsis="true"
@ -97,10 +97,11 @@ export default {
.query({
query: searchUsers,
variables: {
searchText: this.criteria,
query: this.criteria,
filters: this.filters,
currentPage: this.currentPage,
pageSize: this.perPage,
filters: this.filters,
order: 'DESC',
},
fetchPolicy: 'no-cache',
})

View File

@ -1,21 +0,0 @@
import { ArgsType, Field, Int } from 'type-graphql'
import { SearchUsersFilters } from '@arg/SearchUsersFilters'
@ArgsType()
export class SearchUsersArgs {
@Field(() => String)
searchText: string
@Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
currentPage?: number
@Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
pageSize?: number
// eslint-disable-next-line type-graphql/wrong-decorator-signature
@Field(() => SearchUsersFilters, { nullable: true, defaultValue: null })
filters?: SearchUsersFilters | null
}

View File

@ -1,12 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class KlickTipp {
constructor(json: any) {
this.newsletterState = json.status === 'Subscribed'
constructor(newsletterState: boolean) {
this.newsletterState = newsletterState
}
@Field(() => Boolean)

View File

@ -2110,7 +2110,7 @@ describe('UserResolver', () => {
describe('search users', () => {
const variablesWithoutTextAndFilters = {
searchText: '',
query: '',
currentPage: 1,
pageSize: 25,
filters: null,

View File

@ -8,22 +8,12 @@ import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
import { UserContact as DbUserContact } from '@entity/UserContact'
import i18n from 'i18n'
import {
Resolver,
Query,
Args,
Arg,
Authorized,
Ctx,
UseMiddleware,
Mutation,
Int,
} from 'type-graphql'
import { Resolver, Query, Args, Arg, Authorized, Ctx, Mutation, Int } from 'type-graphql'
import { v4 as uuidv4 } from 'uuid'
import { CreateUserArgs } from '@arg/CreateUserArgs'
import { Paginated } from '@arg/Paginated'
import { SearchUsersArgs } from '@arg/SearchUsersArgs'
import { SearchUsersFilters } from '@arg/SearchUsersFilters'
import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs'
import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs'
import { OptInType } from '@enum/OptInType'
@ -60,7 +50,6 @@ import {
EVENT_ADMIN_USER_DELETE,
EVENT_ADMIN_USER_UNDELETE,
} from '@/event/Events'
import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddleware'
import { isValidPassword } from '@/password/EncryptorUtils'
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
import { Context, getUser, getClientTimezoneOffset } from '@/server/context'
@ -76,6 +65,7 @@ import { randombytes_random } from 'sodium-native'
import { FULL_CREATION_AVAILABLE } from './const/const'
import { getUserCreations } from './util/creations'
import { findUserByIdentifier } from './util/findUserByIdentifier'
import { getKlicktippState } from './util/getKlicktippState'
import { validateAlias } from './util/validateAlias'
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
@ -120,7 +110,6 @@ const newGradidoID = async (): Promise<string> => {
export class UserResolver {
@Authorized([RIGHTS.VERIFY_LOGIN])
@Query(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async verifyLogin(@Ctx() context: Context): Promise<User> {
logger.info('verifyLogin...')
// TODO refactor and do not have duplicate code with login(see below)
@ -130,12 +119,12 @@ export class UserResolver {
user.hasElopage = await this.hasElopage(context)
logger.debug(`verifyLogin... successful: ${user.firstName}.${user.lastName}`)
user.klickTipp = await getKlicktippState(userEntity.emailContact.email)
return user
}
@Authorized([RIGHTS.LOGIN])
@Mutation(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async login(
@Args() { email, password, publisherId }: UnsecureLoginArgs,
@Ctx() context: Context,
@ -181,6 +170,7 @@ export class UserResolver {
dbUser.publisherId = publisherId
await DbUser.save(dbUser)
}
user.klickTipp = await getKlicktippState(dbUser.emailContact.email)
context.setHeaders.push({
key: 'token',
@ -638,8 +628,11 @@ export class UserResolver {
@Authorized([RIGHTS.SEARCH_USERS])
@Query(() => SearchUsersResult)
async searchUsers(
@Arg('query', () => String) query: string,
@Arg('filters', () => SearchUsersFilters, { nullable: true })
filters: SearchUsersFilters | null | undefined,
@Args()
{ searchText, currentPage = 1, pageSize = 25, filters }: SearchUsersArgs,
{ currentPage = 1, pageSize = 25, order = Order.ASC }: Paginated,
@Ctx() context: Context,
): Promise<SearchUsersResult> {
const clientTimezoneOffset = getClientTimezoneOffset(context)
@ -657,15 +650,16 @@ export class UserResolver {
userFields.map((fieldName) => {
return 'user.' + fieldName
}),
searchText,
query,
filters ?? null,
currentPage,
pageSize,
order,
)
if (users.length === 0) {
return {
userCount: 0,
userCount: count,
userList: [],
}
}

View File

@ -0,0 +1,19 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { KlickTipp } from '@model/KlickTipp'
import { getKlickTippUser } from '@/apis/KlicktippController'
import { klickTippLogger as logger } from '@/server/logger'
export const getKlicktippState = async (email: string): Promise<KlickTipp> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const klickTippUser = await getKlickTippUser(email)
if (klickTippUser) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return new KlickTipp(klickTippUser.status === 'Subscribed')
}
} catch (err) {
logger.error('There is no klicktipp user for email', email, err)
}
return new KlickTipp(false)
}

View File

@ -1,31 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { MiddlewareFn } from 'type-graphql'
import { KlickTipp } from '@model/KlickTipp'
import { getKlickTippUser } from '@/apis/KlicktippController'
import { klickTippLogger as logger } from '@/server/logger'
export const klicktippNewsletterStateMiddleware: MiddlewareFn = async (
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
{ root, args, context, info },
next,
) => {
// eslint-disable-next-line n/callback-return
const result = await next()
let klickTipp = new KlickTipp({ status: 'Unsubscribed' })
try {
const klickTippUser = await getKlickTippUser(result.email)
if (klickTippUser) {
klickTipp = new KlickTipp(klickTippUser)
}
} catch (err) {
logger.error(`There is no user for (email='${result.email}') ${err}`)
}
result.klickTipp = klickTipp
return result
}

View File

@ -69,12 +69,19 @@ export const sendResetPasswordEmail = gql`
`
export const searchUsers = gql`
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $filters: SearchUsersFilters) {
query (
$query: String!
$filters: SearchUsersFilters
$currentPage: Int = 1
$pageSize: Int = 25
$order: Order = ASC
) {
searchUsers(
searchText: $searchText
query: $query
filters: $filters
currentPage: $currentPage
pageSize: $pageSize
filters: $filters
order: $order
) {
userCount
userList {

View File

@ -1,7 +1,8 @@
import { Brackets, EntityRepository, IsNull, Not, Repository } from '@dbTools/typeorm'
import { User as DbUser } from '@entity/User'
import { SearchUsersFilters } from '@/graphql/arg/SearchUsersFilters'
import { SearchUsersFilters } from '@arg/SearchUsersFilters'
import { Order } from '@enum/Order'
@EntityRepository(DbUser)
export class UserRepository extends Repository<DbUser> {
@ -11,6 +12,7 @@ export class UserRepository extends Repository<DbUser> {
filters: SearchUsersFilters | null,
currentPage: number,
pageSize: number,
order = Order.ASC,
): Promise<[DbUser[], number]> {
const query = this.createQueryBuilder('user')
.select(select)
@ -46,6 +48,7 @@ export class UserRepository extends Repository<DbUser> {
}
return query
.orderBy({ 'user.id': order })
.take(pageSize)
.skip((currentPage - 1) * pageSize)
.getManyAndCount()