Merge branch 'master' into apollo_listGDTTransactions

This commit is contained in:
Einhornimmond 2021-09-24 11:43:11 +02:00
commit 1830b277f8
27 changed files with 127 additions and 117 deletions

View File

@ -1,6 +1,5 @@
name: gradido test CI name: gradido test CI
on: [push] on: [push]
jobs: jobs:

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Resolver, Query, Args, Authorized, Ctx } from 'type-graphql' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
import CONFIG from '../../config' import CONFIG from '../../config'
import { TransactionList } from '../models/Transaction' import { TransactionList } from '../models/Transaction'
import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput' import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput'
@ -23,7 +23,7 @@ export class TransactionResolver {
} }
@Authorized() @Authorized()
@Query(() => String) @Mutation(() => String)
async sendCoins( async sendCoins(
@Args() { email, amount, memo }: TransactionSendArgs, @Args() { email, amount, memo }: TransactionSendArgs,
@Ctx() context: any, @Ctx() context: any,

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware } from 'type-graphql' import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
import CONFIG from '../../config' import CONFIG from '../../config'
import { CheckUsernameResponse } from '../models/CheckUsernameResponse' import { CheckUsernameResponse } from '../models/CheckUsernameResponse'
import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode' import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode'
@ -66,8 +66,8 @@ export class UserResolver {
return 'success' return 'success'
} }
@Query(() => String) @Mutation(() => String)
async create( async createUser(
@Args() { email, firstName, lastName, password, language }: CreateUserArgs, @Args() { email, firstName, lastName, password, language }: CreateUserArgs,
): Promise<string> { ): Promise<string> {
const payload = { const payload = {
@ -104,7 +104,7 @@ export class UserResolver {
return new SendPasswordResetEmailResponse(response.data) return new SendPasswordResetEmailResponse(response.data)
} }
@Query(() => String) @Mutation(() => String)
async resetPassword( async resetPassword(
@Args() @Args()
{ sessionId, email, password }: ChangePasswordArgs, { sessionId, email, password }: ChangePasswordArgs,
@ -122,7 +122,7 @@ export class UserResolver {
} }
@Authorized() @Authorized()
@Query(() => UpdateUserInfosResponse) @Mutation(() => UpdateUserInfosResponse)
async updateUserInfos( async updateUserInfos(
@Args() @Args()
{ {

View File

@ -5,7 +5,7 @@ import jwt from 'jsonwebtoken'
import CONFIG from '../config/' import CONFIG from '../config/'
export default (token: string): any => { export default (token: string): any => {
if (!token) return null if (!token) return new Error('401 Unauthorized')
let sessionId = null let sessionId = null
try { try {
const decoded = jwt.verify(token, CONFIG.JWT_SECRET) const decoded = jwt.verify(token, CONFIG.JWT_SECRET)
@ -15,6 +15,6 @@ export default (token: string): any => {
sessionId, sessionId,
} }
} catch (err) { } catch (err) {
return null throw new Error('403.13 - Client certificate revoked')
} }
} }

View File

@ -3,7 +3,7 @@ import LanguageSwitch from './LanguageSwitch'
const localVue = global.localVue const localVue = global.localVue
const updateUserInfosQueryMock = jest.fn().mockResolvedValue({ const updateUserInfosMutationMock = jest.fn().mockResolvedValue({
data: { data: {
updateUserInfos: { updateUserInfos: {
validValues: 1, validValues: 1,
@ -28,7 +28,7 @@ describe('LanguageSwitch', () => {
locale: 'en', locale: 'en',
}, },
$apollo: { $apollo: {
query: updateUserInfosQueryMock, mutate: updateUserInfosMutationMock,
}, },
} }
@ -119,7 +119,7 @@ describe('LanguageSwitch', () => {
describe('calls the API', () => { describe('calls the API', () => {
it("with locale 'en'", () => { it("with locale 'en'", () => {
wrapper.findAll('li').at(0).find('a').trigger('click') wrapper.findAll('li').at(0).find('a').trigger('click')
expect(updateUserInfosQueryMock).toBeCalledWith( expect(updateUserInfosMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'he@ho.he', email: 'he@ho.he',
@ -131,7 +131,7 @@ describe('LanguageSwitch', () => {
it("with locale 'de'", () => { it("with locale 'de'", () => {
wrapper.findAll('li').at(1).find('a').trigger('click') wrapper.findAll('li').at(1).find('a').trigger('click')
expect(updateUserInfosQueryMock).toBeCalledWith( expect(updateUserInfosMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'he@ho.he', email: 'he@ho.he',

View File

@ -14,7 +14,7 @@
<script> <script>
import { localeChanged } from 'vee-validate' import { localeChanged } from 'vee-validate'
import locales from '../locales/' import locales from '../locales/'
import { updateUserInfos } from '../graphql/queries' import { updateUserInfos } from '../graphql/mutations'
export default { export default {
name: 'LanguageSwitch', name: 'LanguageSwitch',
@ -36,8 +36,8 @@ export default {
this.setLocale(locale) this.setLocale(locale)
if (this.$store.state.email) { if (this.$store.state.email) {
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
locale: locale, locale: locale,

View File

@ -11,3 +11,59 @@ export const unsubscribeNewsletter = gql`
unsubscribeNewsletter(email: $email) unsubscribeNewsletter(email: $email)
} }
` `
export const resetPassword = gql`
mutation($sessionId: Float!, $email: String!, $password: String!) {
resetPassword(sessionId: $sessionId, email: $email, password: $password)
}
`
export const updateUserInfos = gql`
mutation(
$email: String!
$firstName: String
$lastName: String
$description: String
$username: String
$password: String
$passwordNew: String
$locale: String
) {
updateUserInfos(
email: $email
firstName: $firstName
lastName: $lastName
description: $description
username: $username
password: $password
passwordNew: $passwordNew
language: $locale
) {
validValues
}
}
`
export const registerUser = gql`
mutation(
$firstName: String!
$lastName: String!
$email: String!
$password: String!
$language: String!
) {
createUser(
email: $email
firstName: $firstName
lastName: $lastName
password: $password
language: $language
)
}
`
export const sendCoins = gql`
mutation($email: String!, $amount: Float!, $memo: String!) {
sendCoins(email: $email, amount: $amount, memo: $memo)
}
`

View File

@ -22,12 +22,6 @@ export const logout = gql`
} }
` `
export const resetPassword = gql`
query($sessionId: Float!, $email: String!, $password: String!) {
resetPassword(sessionId: $sessionId, email: $email, password: $password)
}
`
export const loginViaEmailVerificationCode = gql` export const loginViaEmailVerificationCode = gql`
query($optin: String!) { query($optin: String!) {
loginViaEmailVerificationCode(optin: $optin) { loginViaEmailVerificationCode(optin: $optin) {
@ -37,32 +31,6 @@ export const loginViaEmailVerificationCode = gql`
} }
` `
export const updateUserInfos = gql`
query(
$email: String!
$firstName: String
$lastName: String
$description: String
$username: String
$password: String
$passwordNew: String
$locale: String
) {
updateUserInfos(
email: $email
firstName: $firstName
lastName: $lastName
description: $description
username: $username
password: $password
passwordNew: $passwordNew
language: $locale
) {
validValues
}
}
`
export const transactionsQuery = gql` export const transactionsQuery = gql`
query($firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") { query($firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") {
transactionList(firstPage: $firstPage, items: $items, order: $order) { transactionList(firstPage: $firstPage, items: $items, order: $order) {
@ -94,30 +62,6 @@ export const transactionsQuery = gql`
} }
` `
export const resgisterUserQuery = gql`
query(
$firstName: String!
$lastName: String!
$email: String!
$password: String!
$language: String!
) {
create(
email: $email
firstName: $firstName
lastName: $lastName
password: $password
language: $language
)
}
`
export const sendCoins = gql`
query($email: String!, $amount: Float!, $memo: String!) {
sendCoins(email: $email, amount: $amount, memo: $memo)
}
`
export const sendResetPasswordEmail = gql` export const sendResetPasswordEmail = gql`
query($email: String!) { query($email: String!) {
sendResetPasswordEmail(email: $email) { sendResetPasswordEmail(email: $email) {

View File

@ -36,7 +36,8 @@
"error": { "error": {
"change-password": "Fehler beim Ändern des Passworts", "change-password": "Fehler beim Ändern des Passworts",
"error": "Fehler", "error": "Fehler",
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!" "no-account": "Leider konnten wir keinen Account finden mit diesen Daten!",
"session-expired": "Sitzung abgelaufen!"
}, },
"form": { "form": {
"amount": "Betrag", "amount": "Betrag",

View File

@ -36,7 +36,8 @@
"error": { "error": {
"change-password": "Error while changing password", "change-password": "Error while changing password",
"error": "Error", "error": "Error",
"no-account": "Unfortunately we could not find an account to the given data!" "no-account": "Unfortunately we could not find an account to the given data!",
"session-expired": "The session expired"
}, },
"form": { "form": {
"amount": "Amount", "amount": "Amount",

View File

@ -21,6 +21,12 @@ const authLink = new ApolloLink((operation, forward) => {
}, },
}) })
return forward(operation).map((response) => { return forward(operation).map((response) => {
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
response.errors[0].message = i18n.t('error.session-expired')
store.dispatch('logout', null)
if (router.currentRoute.path !== '/login') router.push('/login')
return response
}
const newToken = operation.getContext().response.headers.get('token') const newToken = operation.getContext().response.headers.get('token')
if (newToken) store.commit('token', newToken) if (newToken) store.commit('token', newToken)
return response return response

View File

@ -25,7 +25,7 @@ describe('AccountOverview', () => {
}, },
}, },
$apollo: { $apollo: {
query: sendMock, mutate: sendMock,
}, },
} }

View File

@ -51,7 +51,7 @@ import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter
import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue' import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue'
import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue' import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue'
import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue' import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue'
import { sendCoins } from '../../graphql/queries.js' import { sendCoins } from '../../graphql/mutations.js'
const EMPTY_TRANSACTION_DATA = { const EMPTY_TRANSACTION_DATA = {
email: '', email: '',
@ -105,8 +105,8 @@ export default {
async sendTransaction() { async sendTransaction() {
this.loading = true this.loading = true
this.$apollo this.$apollo
.query({ .mutate({
query: sendCoins, mutation: sendCoins,
variables: this.transactionData, variables: this.transactionData,
}) })
.then(() => { .then(() => {

View File

@ -5,7 +5,7 @@ import Register from './Register'
const localVue = global.localVue const localVue = global.localVue
const resgisterUserQueryMock = jest.fn() const registerUserMutationMock = jest.fn()
const routerPushMock = jest.fn() const routerPushMock = jest.fn()
describe('Register', () => { describe('Register', () => {
@ -20,10 +20,11 @@ describe('Register', () => {
push: routerPushMock, push: routerPushMock,
}, },
$apollo: { $apollo: {
query: resgisterUserQueryMock, mutate: registerUserMutationMock,
}, },
$store: { $store: {
state: { state: {
email: 'peter@lustig.de',
language: null, language: null,
}, },
}, },
@ -192,7 +193,7 @@ describe('Register', () => {
describe('server sends back error', () => { describe('server sends back error', () => {
beforeEach(async () => { beforeEach(async () => {
resgisterUserQueryMock.mockRejectedValue({ message: 'Ouch!' }) registerUserMutationMock.mockRejectedValue({ message: 'Ouch!' })
await wrapper.find('form').trigger('submit') await wrapper.find('form').trigger('submit')
await flushPromises() await flushPromises()
}) })
@ -217,7 +218,7 @@ describe('Register', () => {
describe('server sends back success', () => { describe('server sends back success', () => {
beforeEach(() => { beforeEach(() => {
resgisterUserQueryMock.mockResolvedValue({ registerUserMutationMock.mockResolvedValue({
data: { data: {
create: 'success', create: 'success',
}, },
@ -227,7 +228,7 @@ describe('Register', () => {
it('routes to "/thx/register"', async () => { it('routes to "/thx/register"', async () => {
await wrapper.find('form').trigger('submit') await wrapper.find('form').trigger('submit')
await flushPromises() await flushPromises()
expect(resgisterUserQueryMock).toBeCalledWith( expect(registerUserMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'max.mustermann@gradido.net', email: 'max.mustermann@gradido.net',

View File

@ -141,7 +141,7 @@
import InputEmail from '../../components/Inputs/InputEmail.vue' import InputEmail from '../../components/Inputs/InputEmail.vue'
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue' import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue'
import LanguageSwitchSelect from '../../components/LanguageSwitchSelect.vue' import LanguageSwitchSelect from '../../components/LanguageSwitchSelect.vue'
import { resgisterUserQuery } from '../../graphql/queries' import { registerUser } from '../../graphql/mutations'
export default { export default {
components: { InputPasswordConfirmation, InputEmail, LanguageSwitchSelect }, components: { InputPasswordConfirmation, InputEmail, LanguageSwitchSelect },
@ -190,8 +190,8 @@ export default {
}, },
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: resgisterUserQuery, mutation: registerUser,
variables: { variables: {
email: this.form.email, email: this.form.email,
firstName: this.form.firstname, firstName: this.form.firstname,

View File

@ -7,6 +7,7 @@ import flushPromises from 'flush-promises'
const localVue = global.localVue const localVue = global.localVue
const apolloQueryMock = jest.fn().mockRejectedValue({ message: 'error' }) const apolloQueryMock = jest.fn().mockRejectedValue({ message: 'error' })
const apolloMutationMock = jest.fn()
const toasterMock = jest.fn() const toasterMock = jest.fn()
const routerPushMock = jest.fn() const routerPushMock = jest.fn()
@ -36,6 +37,7 @@ describe('ResetPassword', () => {
}), }),
}, },
$apollo: { $apollo: {
mutate: apolloMutationMock,
query: apolloQueryMock, query: apolloQueryMock,
}, },
} }
@ -146,7 +148,7 @@ describe('ResetPassword', () => {
describe('server response with error', () => { describe('server response with error', () => {
beforeEach(() => { beforeEach(() => {
apolloQueryMock.mockRejectedValue({ message: 'error' }) apolloMutationMock.mockRejectedValue({ message: 'error' })
}) })
it('toasts an error message', () => { it('toasts an error message', () => {
expect(toasterMock).toHaveBeenCalledWith('error') expect(toasterMock).toHaveBeenCalledWith('error')
@ -155,14 +157,14 @@ describe('ResetPassword', () => {
describe('server response with success', () => { describe('server response with success', () => {
beforeEach(() => { beforeEach(() => {
apolloQueryMock.mockResolvedValue({ apolloMutationMock.mockResolvedValue({
data: { data: {
resetPassword: 'success', resetPassword: 'success',
}, },
}) })
}) })
it('calls the API', () => { it('calls the API', () => {
expect(apolloQueryMock).toBeCalledWith( expect(apolloMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
sessionId: 1, sessionId: 1,

View File

@ -48,7 +48,8 @@
</template> </template>
<script> <script>
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation' import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation'
import { resetPassword, loginViaEmailVerificationCode } from '../../graphql/queries' import { loginViaEmailVerificationCode } from '../../graphql/queries'
import { resetPassword } from '../../graphql/mutations'
export default { export default {
name: 'ResetPassword', name: 'ResetPassword',
@ -71,8 +72,8 @@ export default {
methods: { methods: {
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: resetPassword, mutation: resetPassword,
variables: { variables: {
sessionId: this.sessionId, sessionId: this.sessionId,
email: this.email, email: this.email,

View File

@ -29,7 +29,7 @@ describe('UserCard_FormUserData', () => {
error: toastErrorMock, error: toastErrorMock,
}, },
$apollo: { $apollo: {
query: mockAPIcall, mutate: mockAPIcall,
}, },
} }

View File

@ -72,7 +72,7 @@
</b-card> </b-card>
</template> </template>
<script> <script>
import { updateUserInfos } from '../../../graphql/queries' import { updateUserInfos } from '../../../graphql/mutations'
export default { export default {
name: 'FormUserData', name: 'FormUserData',
@ -108,8 +108,8 @@ export default {
async onSubmit(event) { async onSubmit(event) {
event.preventDefault() event.preventDefault()
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
firstName: this.form.firstName, firstName: this.form.firstName,

View File

@ -21,7 +21,7 @@ describe('UserCard_FormUserMail', () => {
}, },
}, },
$apollo: { $apollo: {
query: mockAPIcall, mutate: mockAPIcall,
}, },
} }

View File

@ -32,7 +32,7 @@
</b-card> </b-card>
</template> </template>
<script> <script>
import { updateUserInfos } from '../../../graphql/queries' import { updateUserInfos } from '../../../graphql/mutations'
export default { export default {
name: 'FormUserMail', name: 'FormUserMail',
@ -45,8 +45,8 @@ export default {
methods: { methods: {
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
newEmail: this.newEmail, newEmail: this.newEmail,

View File

@ -25,7 +25,7 @@ describe('UserCard_FormUserPasswort', () => {
error: toastErrorMock, error: toastErrorMock,
}, },
$apollo: { $apollo: {
query: changePasswordProfileMock, mutate: changePasswordProfileMock,
}, },
} }

View File

@ -42,7 +42,7 @@
<script> <script>
import InputPassword from '../../../components/Inputs/InputPassword' import InputPassword from '../../../components/Inputs/InputPassword'
import InputPasswordConfirmation from '../../../components/Inputs/InputPasswordConfirmation' import InputPasswordConfirmation from '../../../components/Inputs/InputPasswordConfirmation'
import { updateUserInfos } from '../../../graphql/queries' import { updateUserInfos } from '../../../graphql/mutations'
export default { export default {
name: 'FormUserPasswort', name: 'FormUserPasswort',
@ -73,8 +73,8 @@ export default {
}, },
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
password: this.form.password, password: this.form.password,

View File

@ -35,7 +35,7 @@ describe('UserCard_FormUsername', () => {
error: toastErrorMock, error: toastErrorMock,
}, },
$apollo: { $apollo: {
query: mockAPIcall, mutate: mockAPIcall,
}, },
} }

View File

@ -67,7 +67,7 @@
</b-card> </b-card>
</template> </template>
<script> <script>
import { updateUserInfos } from '../../../graphql/queries' import { updateUserInfos } from '../../../graphql/mutations'
export default { export default {
name: 'FormUsername', name: 'FormUsername',
@ -87,8 +87,8 @@ export default {
}, },
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
username: this.form.username, username: this.form.username,

View File

@ -25,7 +25,7 @@ describe('UserCard_Language', () => {
error: toastErrorMock, error: toastErrorMock,
}, },
$apollo: { $apollo: {
query: mockAPIcall, mutate: mockAPIcall,
}, },
} }

View File

@ -46,7 +46,6 @@
<div class="text-right" ref="submitButton"> <div class="text-right" ref="submitButton">
<b-button <b-button
:variant="loading ? 'default' : 'success'" :variant="loading ? 'default' : 'success'"
@click="onSubmit"
type="submit" type="submit"
class="mt-4" class="mt-4"
:disabled="loading" :disabled="loading"
@ -64,7 +63,7 @@
<script> <script>
import { localeChanged } from 'vee-validate' import { localeChanged } from 'vee-validate'
import LanguageSwitchSelect from '../../../components/LanguageSwitchSelect.vue' import LanguageSwitchSelect from '../../../components/LanguageSwitchSelect.vue'
import { updateUserInfos } from '../../../graphql/queries' import { updateUserInfos } from '../../../graphql/mutations'
export default { export default {
name: 'FormUserLanguage', name: 'FormUserLanguage',
@ -88,24 +87,24 @@ export default {
cancelEdit() { cancelEdit() {
this.showLanguage = true this.showLanguage = true
}, },
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
.query({ .mutate({
query: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email, email: this.$store.state.email,
locale: this.language, locale: this.language,
}, },
}) })
.then(() => { .then(() => {
this.$store.commit('language', this.language)
this.$i18n.locale = this.language this.$i18n.locale = this.language
this.$store.commit('language', this.$i18n.locale) localeChanged(this.language)
localeChanged(this.$i18n.locale)
this.cancelEdit() this.cancelEdit()
this.$toasted.success(this.$t('languages.success')) this.$toasted.success(this.$t('languages.success'))
}) })
.catch((error) => { .catch((error) => {
this.language = this.$store.state.language
this.$toasted.error(error.message) this.$toasted.error(error.message)
}) })
}, },