diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index de9939101..1049aaccd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -386,7 +386,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./coverage/lcov.info - min_coverage: 4 + min_coverage: 3 token: ${{ github.token }} ############################################################################## diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 486cb023b..3d09e56eb 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -1,4 +1,4 @@ -import { ArgsType, Field } from 'type-graphql' +import { ArgsType, Field, Int } from 'type-graphql' @ArgsType() export default class CreateUserArgs { @@ -16,4 +16,7 @@ export default class CreateUserArgs { @Field(() => String) language: string + + @Field(() => Int, { nullable: true }) + publisherId: number } diff --git a/backend/src/graphql/arg/UnsecureLoginArgs.ts b/backend/src/graphql/arg/UnsecureLoginArgs.ts index 9e9cde0d9..ac69e7441 100644 --- a/backend/src/graphql/arg/UnsecureLoginArgs.ts +++ b/backend/src/graphql/arg/UnsecureLoginArgs.ts @@ -1,4 +1,4 @@ -import { ArgsType, Field } from 'type-graphql' +import { ArgsType, Field, Int } from 'type-graphql' @ArgsType() export default class UnsecureLoginArgs { @@ -7,4 +7,7 @@ export default class UnsecureLoginArgs { @Field(() => String) password: string + + @Field(() => Int, { nullable: true }) + publisherId: number } diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index ebdf0aad2..08651ae17 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field } from 'type-graphql' +import { ObjectType, Field, Int } from 'type-graphql' import { KlickTipp } from './KlickTipp' @ObjectType() @@ -19,7 +19,6 @@ export class User { this.pubkey = json.public_hex this.language = json.language this.publisherId = json.publisher_id - if (json.hasElopage) this.hasElopage = json.hasElopage } @Field(() => String) @@ -67,8 +66,8 @@ export class User { groupId: number */ // what is publisherId? - @Field(() => Number) - publisherId: number + @Field(() => Int, { nullable: true }) + publisherId?: number @Field(() => Boolean) coinanimation: boolean @@ -76,6 +75,6 @@ export class User { @Field(() => KlickTipp) klickTipp: KlickTipp - @Field(() => Boolean) + @Field(() => Boolean, { nullable: true }) hasElopage?: boolean } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b2d6d89d9..93719da9a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -31,7 +31,10 @@ import { UserRepository } from '../../typeorm/repository/User' export class UserResolver { @Query(() => User) @UseMiddleware(klicktippNewsletterStateMiddleware) - async login(@Args() { email, password }: UnsecureLoginArgs, @Ctx() context: any): Promise { + async login( + @Args() { email, password, publisherId }: UnsecureLoginArgs, + @Ctx() context: any, + ): Promise { email = email.trim().toLowerCase() const result = await apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', { email, password }) @@ -45,6 +48,11 @@ export class UserResolver { value: encode(result.data.session_id, result.data.user.public_hex), }) const user = new User(result.data.user) + // Hack: Database Field is not validated properly and not nullable + if (user.publisherId === 0) { + user.publisherId = undefined + } + user.hasElopage = result.data.hasElopage // read additional settings from settings table const userRepository = getCustomRepository(UserRepository) let userEntity: void | DbUser @@ -64,6 +72,15 @@ export class UserResolver { throw new Error('error with cannot happen') } + // Save publisherId if Elopage is not yet registered + if (!user.hasElopage && publisherId) { + user.publisherId = publisherId + await this.updateUserInfos( + { publisherId }, + { sessionId: result.data.session_id, pubKey: result.data.user.public_hex }, + ) + } + const userSettingRepository = getCustomRepository(UserSettingRepository) const coinanimation = await userSettingRepository .readBoolean(userEntity.id, Setting.COIN_ANIMATION) @@ -102,7 +119,7 @@ export class UserResolver { @Mutation(() => String) async createUser( - @Args() { email, firstName, lastName, password, language }: CreateUserArgs, + @Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs, ): Promise { const payload = { email, @@ -112,7 +129,7 @@ export class UserResolver { emailType: 2, login_after_register: true, language: language, - publisher_id: 0, + publisher_id: publisherId, } const result = await apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload) if (!result.success) { diff --git a/backend/src/middleware/klicktippMiddleware.ts b/backend/src/middleware/klicktippMiddleware.ts index ab3a9bd4c..e81087097 100644 --- a/backend/src/middleware/klicktippMiddleware.ts +++ b/backend/src/middleware/klicktippMiddleware.ts @@ -24,10 +24,12 @@ export const klicktippNewsletterStateMiddleware: MiddlewareFn = async ( const result = await next() let klickTipp = new KlickTipp({ status: 'Unsubscribed' }) if (CONFIG.KLICKTIPP) { - const klickTippUser = await getKlickTippUser(result.email) - if (klickTippUser) { - klickTipp = new KlickTipp(klickTippUser) - } + try { + const klickTippUser = await getKlickTippUser(result.email) + if (klickTippUser) { + klickTipp = new KlickTipp(klickTippUser) + } + } catch (err) {} } result.klickTipp = klickTipp return result diff --git a/frontend/.env.dist b/frontend/.env.dist index a7d67f970..90f968bd0 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,3 +1,4 @@ ALLOW_REGISTER=true GRAPHQL_URI=http://localhost:4000/graphql +DEFAULT_PUBLISHER_ID=2896 //BUILD_COMMIT=0000000 \ No newline at end of file diff --git a/frontend/src/components/SidebarPlugin/SideBar.spec.js b/frontend/src/components/SidebarPlugin/SideBar.spec.js index 9f182ca93..e42369e50 100644 --- a/frontend/src/components/SidebarPlugin/SideBar.spec.js +++ b/frontend/src/components/SidebarPlugin/SideBar.spec.js @@ -18,6 +18,10 @@ describe('SideBar', () => { $store: { state: { email: 'test@example.org', + publisherId: 123, + firstName: 'test', + lastName: 'example', + hasElopage: false, }, commit: jest.fn(), }, @@ -78,14 +82,18 @@ describe('SideBar', () => { }) describe('static menu items', () => { - describe("member's area", () => { + describe("member's area without publisher ID", () => { it('has a link to the elopage', () => { - expect(wrapper.findAll('li').at(0).text()).toBe('members_area') + expect(wrapper.findAll('li').at(0).text()).toContain('members_area') }) - it('links to the elopage', () => { + it('has a badge', () => { + expect(wrapper.findAll('li').at(0).text()).toContain('!') + }) + + it('links to the elopage registration', () => { expect(wrapper.findAll('li').at(0).find('a').attributes('href')).toBe( - 'https://elopage.com/s/gradido/sign_in?locale=en', + 'https://elopage.com/s/gradido/basic-de/payment?locale=en&prid=111&pid=123&firstName=test&lastName=example&email=test@example.org', ) }) @@ -94,11 +102,27 @@ describe('SideBar', () => { mocks.$i18n.locale = 'de' }) - it('links to the German elopage when locale is set to de', () => { + it('links to the German elopage registration when locale is set to de', () => { + expect(wrapper.findAll('li').at(0).find('a').attributes('href')).toBe( + 'https://elopage.com/s/gradido/basic-de/payment?locale=de&prid=111&pid=123&firstName=test&lastName=example&email=test@example.org', + ) + }) + }) + + describe("member's area with publisher ID", () => { + beforeEach(() => { + mocks.$store.state.hasElopage = true + }) + + it('links to the elopage member area', () => { expect(wrapper.findAll('li').at(0).find('a').attributes('href')).toBe( 'https://elopage.com/s/gradido/sign_in?locale=de', ) }) + + it('has no badge', () => { + expect(wrapper.findAll('li').at(0).text()).not.toContain('!') + }) }) }) diff --git a/frontend/src/components/SidebarPlugin/SideBar.vue b/frontend/src/components/SidebarPlugin/SideBar.vue index 519a4f0b1..8f153cb11 100755 --- a/frontend/src/components/SidebarPlugin/SideBar.vue +++ b/frontend/src/components/SidebarPlugin/SideBar.vue @@ -47,12 +47,9 @@
@@ -71,6 +68,7 @@ diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 96f7795ce..f04251029 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -15,6 +15,7 @@ const environment = { NODE_ENV: process.env.NODE_ENV, DEBUG: process.env.NODE_ENV !== 'production' || false, PRODUCTION: process.env.NODE_ENV === 'production' || false, + DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID || 2896, } const server = { diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index d5e9c4228..480576caf 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -51,6 +51,7 @@ export const registerUser = gql` $email: String! $password: String! $language: String! + $publisherId: Int! ) { createUser( email: $email @@ -58,6 +59,7 @@ export const registerUser = gql` lastName: $lastName password: $password language: $language + publisherId: $publisherId ) } ` diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 3499a3fa1..2ee381dd8 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -1,8 +1,8 @@ import gql from 'graphql-tag' export const login = gql` - query($email: String!, $password: String!) { - login(email: $email, password: $password) { + query($email: String!, $password: String!, $publisherId: Int) { + login(email: $email, password: $password, publisherId: $publisherId) { email username firstName @@ -13,6 +13,8 @@ export const login = gql` klickTipp { newsletterState } + hasElopage + publisherId } } ` diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 03e1915ca..9f87f0398 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -30,7 +30,9 @@ export const mutations = { state.newsletterState = newsletterState }, publisherId: (state, publisherId) => { - state.publisherId = publisherId + let pubId = parseInt(publisherId) + if (isNaN(pubId)) pubId = null + state.publisherId = pubId }, community: (state, community) => { state.community = community @@ -38,6 +40,9 @@ export const mutations = { coinanimation: (state, coinanimation) => { state.coinanimation = coinanimation }, + hasElopage: (state, hasElopage) => { + state.hasElopage = hasElopage + }, } export const actions = { @@ -50,6 +55,8 @@ export const actions = { commit('description', data.description) commit('coinanimation', data.coinanimation) commit('newsletterState', data.klickTipp.newsletterState) + commit('hasElopage', data.hasElopage) + commit('publisherId', data.publisherId) }, logout: ({ commit, state }) => { commit('token', null) @@ -60,6 +67,8 @@ export const actions = { commit('description', '') commit('coinanimation', true) commit('newsletterState', null) + commit('hasElopage', false) + commit('publisherId', null) localStorage.clear() }, } @@ -81,6 +90,8 @@ export const store = new Vuex.Store({ coinanimation: true, newsletterState: null, community: null, + hasElopage: false, + publisherId: null, }, getters: {}, // Syncronous mutation of the state diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 19937eed4..bdb98d03b 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -12,6 +12,7 @@ const { newsletterState, publisherId, community, + hasElopage, } = mutations const { login, logout } = actions @@ -95,6 +96,12 @@ describe('Vuex store', () => { publisherId(state, 42) expect(state.publisherId).toEqual(42) }) + + it('sets publisherId to null with NaN', () => { + const state = {} + publisherId(state, 'abc') + expect(state.publisherId).toEqual(null) + }) }) describe('community', () => { @@ -114,6 +121,14 @@ describe('Vuex store', () => { }) }) }) + + describe('hasElopage', () => { + it('sets the state of hasElopage', () => { + const state = { hasElopage: false } + hasElopage(state, true) + expect(state.hasElopage).toBeTruthy() + }) + }) }) describe('actions', () => { @@ -131,11 +146,13 @@ describe('Vuex store', () => { klickTipp: { newsletterState: true, }, + hasElopage: false, + publisherId: 1234, } - it('calls eight commits', () => { + it('calls ten commits', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(10) }) it('commits email', () => { @@ -177,15 +194,25 @@ describe('Vuex store', () => { login({ commit, state }, commitedData) expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', true) }) + + it('commits hasElopage', () => { + login({ commit, state }, commitedData) + expect(commit).toHaveBeenNthCalledWith(9, 'hasElopage', false) + }) + + it('commits publisherId', () => { + login({ commit, state }, commitedData) + expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', 1234) + }) }) describe('logout', () => { const commit = jest.fn() const state = {} - it('calls eight commits', () => { + it('calls ten commits', () => { logout({ commit, state }) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(10) }) it('commits token', () => { @@ -228,6 +255,16 @@ describe('Vuex store', () => { expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', null) }) + it('commits hasElopage', () => { + logout({ commit, state }) + expect(commit).toHaveBeenNthCalledWith(9, 'hasElopage', false) + }) + + it('commits publisherId', () => { + logout({ commit, state }) + expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', null) + }) + // how to get this working? it.skip('calls localStorage.clear()', () => { const clearStorageMock = jest.fn() diff --git a/frontend/src/views/Layout/DashboardLayout_gdd.spec.js b/frontend/src/views/Layout/DashboardLayout_gdd.spec.js index 6555d1dda..107ee7f27 100644 --- a/frontend/src/views/Layout/DashboardLayout_gdd.spec.js +++ b/frontend/src/views/Layout/DashboardLayout_gdd.spec.js @@ -45,6 +45,9 @@ describe('DashboardLayoutGdd', () => { $store: { state: { email: 'user@example.org', + publisherId: 123, + firstName: 'User', + lastName: 'Example', }, dispatch: storeDispatchMock, commit: storeCommitMock, @@ -114,9 +117,10 @@ describe('DashboardLayoutGdd', () => { }) it('has a link to the members area', () => { - expect(wrapper.findAll('ul').at(2).text()).toBe('members_area') + expect(wrapper.findAll('ul').at(2).text()).toContain('members_area') + expect(wrapper.findAll('ul').at(2).text()).toContain('!') expect(wrapper.findAll('ul').at(2).find('a').attributes('href')).toBe( - 'https://elopage.com/s/gradido/sign_in?locale=en', + 'https://elopage.com/s/gradido/basic-de/payment?locale=en&prid=111&pid=123&firstName=User&lastName=Example&email=user@example.org', ) }) diff --git a/frontend/src/views/Pages/Login.spec.js b/frontend/src/views/Pages/Login.spec.js index 6aac3fda2..82f92da91 100644 --- a/frontend/src/views/Pages/Login.spec.js +++ b/frontend/src/views/Pages/Login.spec.js @@ -44,6 +44,7 @@ describe('Login', () => { registerUrl: 'http://localhost/vue/register', description: 'Die lokale Entwicklungsumgebung von Gradido.', }, + publisherId: 12345, }, }, $loading: { @@ -199,6 +200,7 @@ describe('Login', () => { variables: { email: 'user@example.org', password: '1234', + publisherId: 12345, }, }), ) diff --git a/frontend/src/views/Pages/Login.vue b/frontend/src/views/Pages/Login.vue index db8789002..6695242e6 100755 --- a/frontend/src/views/Pages/Login.vue +++ b/frontend/src/views/Pages/Login.vue @@ -96,6 +96,7 @@ export default { variables: { email: this.form.email, password: this.form.password, + publisherId: this.$store.state.publisherId, }, fetchPolicy: 'network-only', }) diff --git a/frontend/src/views/Pages/Register.spec.js b/frontend/src/views/Pages/Register.spec.js index 388a96746..98db5e18c 100644 --- a/frontend/src/views/Pages/Register.spec.js +++ b/frontend/src/views/Pages/Register.spec.js @@ -32,6 +32,7 @@ describe('Register', () => { registerUrl: 'http://localhost/vue/register', description: 'Die lokale Entwicklungsumgebung von Gradido.', }, + publisherId: 12345, }, }, } @@ -216,6 +217,7 @@ describe('Register', () => { lastName: 'Mustermann', password: 'Aa123456_', language: 'en', + publisherId: 12345, }, }), ) diff --git a/frontend/src/views/Pages/Register.vue b/frontend/src/views/Pages/Register.vue index 8e1a5d26d..863dde988 100755 --- a/frontend/src/views/Pages/Register.vue +++ b/frontend/src/views/Pages/Register.vue @@ -199,6 +199,7 @@ export default { lastName: this.form.lastname, password: this.form.password.password, language: this.language, + publisherId: this.$store.state.publisherId, }, }) .then(() => { diff --git a/login_server/src/cpp/JSONInterface/JsonHasElopage.cpp b/login_server/src/cpp/JSONInterface/JsonHasElopage.cpp index ee66255be..202f45fca 100644 --- a/login_server/src/cpp/JSONInterface/JsonHasElopage.cpp +++ b/login_server/src/cpp/JSONInterface/JsonHasElopage.cpp @@ -10,7 +10,7 @@ Poco::JSON::Object* JsonHasElopage::handle(Poco::Dynamic::Var params) auto elopage_buy = Poco::AutoPtr(new model::table::ElopageBuy); result = stateSuccess(); - result->set("hasElopage", elopage_buy->isExistInDB("email", mSession->getNewUser()->getModel()->getEmail())); + result->set("hasElopage", elopage_buy->isExistInDB("payer_email", mSession->getNewUser()->getModel()->getEmail())); return result; } \ No newline at end of file diff --git a/login_server/src/cpp/model/table/ElopageBuy.cpp b/login_server/src/cpp/model/table/ElopageBuy.cpp index ff79f6a68..9aeaf291f 100644 --- a/login_server/src/cpp/model/table/ElopageBuy.cpp +++ b/login_server/src/cpp/model/table/ElopageBuy.cpp @@ -113,7 +113,7 @@ namespace model { int UserHasElopageTask::run() { auto elopage_buy = Poco::AutoPtr(new model::table::ElopageBuy); - bool hasElopage = elopage_buy->isExistInDB("payer_email", mEmail); + mHasElopage = elopage_buy->isExistInDB("payer_email", mEmail); return 0; } }