mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge remote-tracking branch 'origin/master' into community_coverage
This commit is contained in:
commit
8198415bdd
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -212,7 +212,7 @@ jobs:
|
||||
report_name: Coverage Frontend
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 20
|
||||
min_coverage: 21
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
|
||||
@ -60,19 +60,21 @@ Router::scope('/', function (RouteBuilder $routes) {
|
||||
$whitelist = ['JsonRequestHandler', 'ElopageWebhook', 'AppRequests'];
|
||||
$ajaxWhitelist = ['TransactionSendCoins', 'TransactionCreations'];
|
||||
|
||||
$callerIp = $request->clientIp();
|
||||
|
||||
foreach($whitelist as $entry) {
|
||||
if($request->getParam('controller') === $entry) {
|
||||
if($entry == 'ElopageWebhook' || $entry == 'AppRequests') {
|
||||
return true;
|
||||
}
|
||||
$allowedIpLocalhost = ['127.0.0.1', 'localhost', '', '::1'];
|
||||
if(in_array($request->clientIp(), $allowedIpLocalhost)) {
|
||||
if(in_array($callerIp, $allowedIpLocalhost)) {
|
||||
return true;
|
||||
}
|
||||
$allowedCaller = Configure::read('API.allowedCaller');
|
||||
$ipPerHost = [];
|
||||
if($allowedCaller && count($allowedCaller) > 0) {
|
||||
$callerIp = $request->clientIp();
|
||||
|
||||
foreach($allowedCaller as $allowed) {
|
||||
$ip = gethostbyname($allowed);
|
||||
$ipPerHost[$allowed] = $ip;
|
||||
|
||||
@ -348,7 +348,7 @@ class AppRequestsController extends AppController
|
||||
$decay = true;
|
||||
$transactions = [];
|
||||
$transactions_from_db = $stateUserTransactionsQuery->toArray();
|
||||
|
||||
|
||||
if(count($transactions_from_db)) {
|
||||
if($orderDirection == 'DESC') {
|
||||
$transactions_from_db = array_reverse($transactions_from_db);
|
||||
|
||||
@ -391,8 +391,12 @@ class JsonRequestHandlerController extends AppController {
|
||||
}
|
||||
|
||||
if ($transaction->save()) {
|
||||
$result = ['state' => 'success'];
|
||||
if($transaction->hasWarnings()) {
|
||||
$result['warnings'] = $transaction->getWarnings();
|
||||
}
|
||||
// success
|
||||
return $this->returnJson(['state' => 'success']);
|
||||
return $this->returnJson($result);
|
||||
} else {
|
||||
|
||||
$this->sendEMailTransactionFailed($transaction, 'save');
|
||||
|
||||
@ -198,8 +198,10 @@ class Transaction extends TransactionBase {
|
||||
|
||||
$connection->commit();
|
||||
|
||||
$this->mTransactionBody->getSpecificTransaction()->sendNotificationEmail($this->mTransactionBody->getMemo());
|
||||
$specificTransaction = $this->mTransactionBody->getSpecificTransaction();
|
||||
|
||||
$specificTransaction->sendNotificationEmail($this->mTransactionBody->getMemo());
|
||||
$this->addWarnings($specificTransaction->getWarnings());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -6,29 +6,43 @@ use Cake\ORM\TableRegistry;
|
||||
|
||||
class TransactionBase {
|
||||
private $errors = [];
|
||||
private $warnings = [];
|
||||
static $tables = [];
|
||||
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function getWarnings() {
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
public function addError($functionName, $errorName) {
|
||||
array_push($this->errors, [$functionName => $errorName]);
|
||||
array_push($this->errors, [$functionName => $errorName]);
|
||||
}
|
||||
public function addWarning($functionName, $warningName) {
|
||||
array_push($this->warnings, [$functionName => $warningName]);
|
||||
}
|
||||
|
||||
public function addErrors($errors) {
|
||||
$this->errors = array_merge($this->errors, $errors);
|
||||
$this->errors = array_merge($this->errors, $errors);
|
||||
}
|
||||
|
||||
public function addWarnings($warnings) {
|
||||
$this->warnings = array_merge($this->warnings, $warnings);
|
||||
}
|
||||
|
||||
public function hasErrors() {
|
||||
return count($this->errors) > 0;
|
||||
return count($this->errors) > 0;
|
||||
}
|
||||
|
||||
public function hasWarnings() {
|
||||
return count($this->warnings) > 0;
|
||||
}
|
||||
public static function getTable($tableName) {
|
||||
if(!isset(self::$tables[$tableName])) {
|
||||
self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName);
|
||||
}
|
||||
return self::$tables[$tableName];
|
||||
if(!isset(self::$tables[$tableName])) {
|
||||
self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName);
|
||||
}
|
||||
return self::$tables[$tableName];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -209,6 +209,7 @@ class TransactionCreation extends TransactionBase {
|
||||
->send();
|
||||
} catch(Exception $e) {
|
||||
// $this->addError('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
|
||||
$this->addWarning('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -204,13 +204,14 @@ class TransactionTransfer extends TransactionBase {
|
||||
$this->addError('TransactionCreation::sendNotificationEmail', 'to email is empty for user: ' . $receiverUser->id);
|
||||
return false;
|
||||
}
|
||||
$email->setFrom([$serverAdminEmail => $senderUser->getNames() . ' via Gradido Community'])
|
||||
$noReplyEmail = Configure::read('noReplyEmail');
|
||||
$email->setFrom([$noReplyEmail => 'Gradido (nicht antworten)'])
|
||||
->setTo([$receiverUser->email => $receiverUser->getNames()])
|
||||
->setReplyTo($senderUser->email)
|
||||
->setSubject(__('Gradidos erhalten'))
|
||||
->send();
|
||||
} catch(Exception $e) {
|
||||
//$this->addError('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
|
||||
$this->addWarning('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -15,7 +15,7 @@ $senderNames = $senderUser->first_name . ' ' . $senderUser->last_name;
|
||||
|
||||
<?= $memo ?>
|
||||
|
||||
<?= __('Du kannst {0} eine Nachricht schreiben, indem du auf diese E-Mail antwortest', $senderNames); ?>
|
||||
<?= __('Bitte antworte nicht auf diese E-Mail!'); ?>
|
||||
|
||||
<?= __('Mit freundlichen Grüßen'); ?>
|
||||
Gradido Community Server
|
||||
@ -214,9 +214,8 @@ return [
|
||||
'timeout' => 30,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'client' => null,
|
||||
'tls' => null,
|
||||
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
|
||||
'className' => 'Smtp',
|
||||
'tls' => true
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@ -89,6 +89,56 @@ In case of success returns:
|
||||
nginx was wrong configured.
|
||||
- `session_id`: can be also negative
|
||||
|
||||
## Check username
|
||||
### Request
|
||||
`GET http://localhost/login_api/checkUsername?username=<username>&group_id=<group_id>`
|
||||
|
||||
`POST http://localhost/login_api/checkUsername`
|
||||
with
|
||||
```json
|
||||
{
|
||||
"username": "Maxilein",
|
||||
"group_id": 1,
|
||||
"group_alias": "gdd1"
|
||||
}
|
||||
```
|
||||
|
||||
group_id or group_alias, one of both is enough.
|
||||
group_id is better, because one db request less
|
||||
|
||||
### Response
|
||||
|
||||
If username is not already taken
|
||||
```json
|
||||
{
|
||||
"state":"success"
|
||||
}
|
||||
```
|
||||
|
||||
If username is already taken
|
||||
```json
|
||||
{
|
||||
"state":"warning",
|
||||
"msg":"username already in use"
|
||||
}
|
||||
```
|
||||
|
||||
If only group_alias was given and group with that alias was found in db
|
||||
```json
|
||||
{
|
||||
"state":"success",
|
||||
"group_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
If group_id or group_alias unknown
|
||||
```json
|
||||
{
|
||||
"state":"error",
|
||||
"msg": "unknown group"
|
||||
}
|
||||
```
|
||||
|
||||
## Create user
|
||||
Register a new User
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ const communityAPI = {
|
||||
balance: async (sessionId) => {
|
||||
return apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId)
|
||||
},
|
||||
transactions: async (sessionId, firstPage = 1, items = 1000, order = 'DESC') => {
|
||||
transactions: async (sessionId, firstPage = 1, items = 5, order = 'DESC') => {
|
||||
return apiGet(
|
||||
`${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`,
|
||||
)
|
||||
|
||||
@ -78,6 +78,27 @@ const loginAPI = {
|
||||
CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin,
|
||||
)
|
||||
},
|
||||
getUserInfos: async (sessionId, email) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email: email,
|
||||
ask: ['user.first_name', 'user.last_name'],
|
||||
}
|
||||
return apiPost(CONFIG.LOGIN_API_URL + 'getUserInfos', payload)
|
||||
},
|
||||
updateUserInfos: async (sessionId, email, data) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email,
|
||||
update: {
|
||||
'User.first_name': data.firstName,
|
||||
'User.last_name': data.lastName,
|
||||
'User.description': data.description,
|
||||
},
|
||||
}
|
||||
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
|
||||
},
|
||||
|
||||
changePassword: async (sessionId, email, password) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
@ -88,6 +109,27 @@ const loginAPI = {
|
||||
}
|
||||
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
|
||||
},
|
||||
changePasswordProfile: async (sessionId, email, password, passwordNew) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email,
|
||||
update: {
|
||||
'User.password': password,
|
||||
'User.passwordNew': passwordNew,
|
||||
},
|
||||
}
|
||||
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
|
||||
},
|
||||
changeUsernameProfile: async (sessionId, email, usernameNew) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
email,
|
||||
update: {
|
||||
'User.usernameNew': usernameNew,
|
||||
},
|
||||
}
|
||||
return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload)
|
||||
},
|
||||
updateLanguage: async (sessionId, email, language) => {
|
||||
const payload = {
|
||||
session_id: sessionId,
|
||||
|
||||
53
frontend/src/components/PaginationButtons.spec.js
Normal file
53
frontend/src/components/PaginationButtons.spec.js
Normal file
@ -0,0 +1,53 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import PaginationButtons from './PaginationButtons'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('PaginationButtons', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(PaginationButtons, { localVue })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div.pagination-buttons').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has previous page button disabled by default', () => {
|
||||
expect(wrapper.find('button.previous-page').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('has bext page button disabled by default', () => {
|
||||
expect(wrapper.find('button.next-page').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('shows the text "1 / 1" by default"', () => {
|
||||
expect(wrapper.find('p.text-center').text()).toBe('1 / 1')
|
||||
})
|
||||
|
||||
describe('with active buttons', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
hasNext: true,
|
||||
hasPrevious: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('emits show-previous when previous page button is clicked', () => {
|
||||
wrapper.find('button.previous-page').trigger('click')
|
||||
expect(wrapper.emitted('show-previous')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('emits show-next when next page button is clicked', () => {
|
||||
wrapper.find('button.next-page').trigger('click')
|
||||
expect(wrapper.emitted('show-next')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
30
frontend/src/components/PaginationButtons.vue
Normal file
30
frontend/src/components/PaginationButtons.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="pagination-buttons">
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-right">
|
||||
<b-button class="previous-page" :disabled="!hasPrevious" @click="$emit('show-previous')">
|
||||
<b-icon icon="chevron-left" variant="primary"></b-icon>
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col cols="2">
|
||||
<p class="text-center pt-2">{{ currentPage }} / {{ totalPages }}</p>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-button class="next-page" :disabled="!hasNext" @click="$emit('show-next')">
|
||||
<b-icon icon="chevron-right" variant="primary"></b-icon>
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'PaginationButtons',
|
||||
props: {
|
||||
hasNext: { type: Boolean, default: false },
|
||||
hasPrevious: { type: Boolean, default: false },
|
||||
totalPages: { type: Number, default: 1 },
|
||||
currentPage: { type: Number, default: 1 },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -20,17 +20,25 @@
|
||||
},
|
||||
"decay": "Vergänglichkeit",
|
||||
"form": {
|
||||
"cancel":"Abbrechen",
|
||||
"cancel": "Abbrechen",
|
||||
"reset": "Zurücksetzen",
|
||||
"close":"schließen",
|
||||
"close": "schließen",
|
||||
"edit": "bearbeiten",
|
||||
"save": "speichern",
|
||||
"receiver":"Empfänger",
|
||||
"sender":"Absender",
|
||||
"username":"Username",
|
||||
"firstname":"Vorname",
|
||||
"lastname":"Nachname",
|
||||
"description": "Beschreibung",
|
||||
"email":"E-Mail",
|
||||
"email_repeat":"eMail wiederholen",
|
||||
"password":"Passwort",
|
||||
"password_repeat":"Passwort wiederholen",
|
||||
"password_old":"altes Passwort",
|
||||
"password_new":"neues Passwort",
|
||||
"password_new_repeat":"neues Passwort wiederholen",
|
||||
"change": "ändern",
|
||||
"amount":"Betrag",
|
||||
"memo":"Nachricht für den Empfänger",
|
||||
"message":"Nachricht",
|
||||
@ -49,8 +57,9 @@
|
||||
"send_transaction_error":"Leider konnte die Transaktion nicht ausgeführt werden!",
|
||||
"validation": {
|
||||
"double": "Das Feld {field} muss eine Dezimalzahl mit zwei Nachkommastellen sein",
|
||||
"is-not": "Du kannst dir selbst keine Gradidos überweisen"
|
||||
}
|
||||
"is-not": "Du kannst Dir selbst keine Gradidos überweisen"
|
||||
},
|
||||
"change_username_info": "Das ändern des Usernamens bedarf mehrerer Schritte."
|
||||
},
|
||||
"error": {
|
||||
"error":"Fehler"
|
||||
@ -96,7 +105,6 @@
|
||||
"add_work":"neuer Gemeinschaftsbeitrag"
|
||||
},
|
||||
"profil": {
|
||||
"transactions":"transactions",
|
||||
"activity": {
|
||||
"chart":"Gemeinschaftsstunden Chart",
|
||||
"new":"Neue Gemeinschaftsstunden eintragen",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"message": "hello gradido !!",
|
||||
"welcome":"Welcome!",
|
||||
"community": "Gemeinschaft",
|
||||
"community": "Community",
|
||||
"logout":"Logout",
|
||||
"login":"Login",
|
||||
"signup": "Sign up",
|
||||
@ -23,14 +23,22 @@
|
||||
"cancel":"Cancel",
|
||||
"reset": "Reset",
|
||||
"close":"Close",
|
||||
"edit": "Edit",
|
||||
"save": "save",
|
||||
"receiver":"Receiver",
|
||||
"sender":"Sender",
|
||||
"username":"Username",
|
||||
"firstname":"Firstname",
|
||||
"lastname":"Lastname",
|
||||
"description": "Description",
|
||||
"email":"Email",
|
||||
"email_repeat":"Repeat Email",
|
||||
"password":"Password",
|
||||
"password_repeat":"Repeat password",
|
||||
"password_old":"Old password",
|
||||
"password_new":"New password",
|
||||
"password_new_repeat":"Repeat new password",
|
||||
"change": "change",
|
||||
"amount":"Amount",
|
||||
"memo":"Message for the recipient",
|
||||
"message":"Message",
|
||||
@ -50,7 +58,8 @@
|
||||
"validation": {
|
||||
"double": "The {field} field must be a decimal with two digits",
|
||||
"is-not": "You cannot send Gradidos to yourself"
|
||||
}
|
||||
},
|
||||
"change_username_info": "Changing the username requires several steps."
|
||||
},
|
||||
"error": {
|
||||
"error":"Error"
|
||||
|
||||
@ -16,25 +16,11 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
component: () => import('../views/Pages/UserProfile.vue'),
|
||||
component: () => import('../views/Pages/UserProfileOverview.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: '/profileedit',
|
||||
// component: () => import('../views/Pages/UserProfileEdit.vue'),
|
||||
// meta: {
|
||||
// requiresAuth: true,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: '/activity',
|
||||
// component: () => import('../views/Pages/UserProfileActivity.vue'),
|
||||
// meta: {
|
||||
// requiresAuth: true,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
path: '/transactions',
|
||||
component: () => import('../views/Pages/UserProfileTransactionList.vue'),
|
||||
|
||||
@ -13,6 +13,18 @@ export const mutations = {
|
||||
sessionId: (state, sessionId) => {
|
||||
state.sessionId = sessionId
|
||||
},
|
||||
username: (state, username) => {
|
||||
state.username = username
|
||||
},
|
||||
firstName: (state, firstName) => {
|
||||
state.firstName = firstName
|
||||
},
|
||||
lastName: (state, lastName) => {
|
||||
state.lastName = lastName
|
||||
},
|
||||
description: (state, description) => {
|
||||
state.description = description
|
||||
},
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
@ -20,10 +32,18 @@ export const actions = {
|
||||
commit('sessionId', data.sessionId)
|
||||
commit('email', data.user.email)
|
||||
commit('language', data.user.language)
|
||||
commit('username', data.user.username)
|
||||
commit('firstName', data.user.first_name)
|
||||
commit('lastName', data.user.last_name)
|
||||
commit('description', data.user.description)
|
||||
},
|
||||
logout: ({ commit, state }) => {
|
||||
commit('sessionId', null)
|
||||
commit('email', null)
|
||||
commit('username', '')
|
||||
commit('firstName', '')
|
||||
commit('lastName', '')
|
||||
commit('description', '')
|
||||
sessionStorage.clear()
|
||||
},
|
||||
}
|
||||
@ -39,6 +59,10 @@ export const store = new Vuex.Store({
|
||||
email: '',
|
||||
language: null,
|
||||
modals: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
username: '',
|
||||
description: '',
|
||||
},
|
||||
getters: {},
|
||||
// Syncronous mutation of the state
|
||||
|
||||
@ -40,7 +40,7 @@ describe('Vuex store', () => {
|
||||
{ commit, state },
|
||||
{ sessionId: 1234, user: { email: 'someone@there.is', language: 'en' } },
|
||||
)
|
||||
expect(commit).toHaveBeenCalledTimes(3)
|
||||
expect(commit).toHaveBeenCalledTimes(7)
|
||||
})
|
||||
|
||||
it('commits sessionId', () => {
|
||||
@ -74,7 +74,7 @@ describe('Vuex store', () => {
|
||||
|
||||
it('calls two commits', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenCalledTimes(2)
|
||||
expect(commit).toHaveBeenCalledTimes(6)
|
||||
})
|
||||
|
||||
it('commits sessionId', () => {
|
||||
|
||||
@ -78,7 +78,7 @@ describe('DashboardLayoutGdd', () => {
|
||||
})
|
||||
|
||||
it('has five items in the navbar', () => {
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(2)
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('has first item "send" in navbar', () => {
|
||||
@ -103,21 +103,21 @@ describe('DashboardLayoutGdd', () => {
|
||||
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/transactions')
|
||||
})
|
||||
|
||||
// it('has tree items in the navbar', () => {
|
||||
// expect(navbar.findAll('ul > li')).toHaveLength(3)
|
||||
// })
|
||||
//
|
||||
// it('has third item "My profile" in navbar', () => {
|
||||
// expect(navbar.findAll('ul > li').at(2).text()).toEqual('site.navbar.my-profil')
|
||||
// })
|
||||
//
|
||||
// it.skip('has third item "My profile" linked to profile in navbar', async () => {
|
||||
// navbar.findAll('ul > li > a').at(2).trigger('click')
|
||||
// await flushPromises()
|
||||
// await jest.runAllTimers()
|
||||
// await flushPromises()
|
||||
// expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/profile')
|
||||
// })
|
||||
it('has tree items in the navbar', () => {
|
||||
expect(navbar.findAll('ul > a')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('has third item "My profile" in navbar', () => {
|
||||
expect(navbar.findAll('ul > a').at(2).text()).toEqual('site.navbar.my-profil')
|
||||
})
|
||||
|
||||
it.skip('has third item "My profile" linked to profile in navbar', async () => {
|
||||
navbar.findAll('ul > a').at(2).trigger('click')
|
||||
await flushPromises()
|
||||
await jest.runAllTimers()
|
||||
await flushPromises()
|
||||
expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/profile')
|
||||
})
|
||||
|
||||
// it('has fourth item "Settigs" in navbar', () => {
|
||||
// expect(navbar.findAll('ul > li').at(3).text()).toEqual('site.navbar.settings')
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<side-bar @logout="logout" :balance="balance" :pending="pending">
|
||||
<template slot="links">
|
||||
<sidebar-item
|
||||
@ -14,18 +14,12 @@
|
||||
path: '/transactions',
|
||||
}"
|
||||
></sidebar-item>
|
||||
|
||||
<!--
|
||||
<b-nav-item href="#!" to="/profile">
|
||||
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.my-profil') }}</b-nav-text>
|
||||
</b-nav-item>
|
||||
<b-nav-item href="#!" to="/profileedit">
|
||||
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.settings') }}</b-nav-text>
|
||||
</b-nav-item>
|
||||
<b-nav-item href="#!" to="/activity">
|
||||
<b-nav-text class="p-0 text-lg text-muted">{{ $t('site.navbar.activity') }}</b-nav-text>
|
||||
</b-nav-item>
|
||||
-->
|
||||
<sidebar-item
|
||||
:link="{
|
||||
name: $t('site.navbar.my-profil'),
|
||||
path: '/profile',
|
||||
}"
|
||||
></sidebar-item>
|
||||
</template>
|
||||
</side-bar>
|
||||
<div class="main-content">
|
||||
@ -39,6 +33,7 @@
|
||||
:transactions="transactions"
|
||||
:transactionCount="transactionCount"
|
||||
:pending="pending"
|
||||
:UserProfileTestData="UserProfileTestData"
|
||||
@update-balance="updateBalance"
|
||||
@update-transactions="updateTransactions"
|
||||
></router-view>
|
||||
@ -90,6 +85,11 @@ export default {
|
||||
bookedBalance: 0,
|
||||
transactionCount: 0,
|
||||
pending: true,
|
||||
UserProfileTestData: {
|
||||
username: 'Mustermax',
|
||||
desc:
|
||||
'Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. Max Mustermann seine Beschreibung. ',
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -106,9 +106,13 @@ export default {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.push('/login')
|
||||
},
|
||||
async updateTransactions() {
|
||||
async updateTransactions(pagination) {
|
||||
this.pending = true
|
||||
const result = await communityAPI.transactions(this.$store.state.sessionId)
|
||||
const result = await communityAPI.transactions(
|
||||
this.$store.state.sessionId,
|
||||
pagination.firstPage,
|
||||
pagination.items,
|
||||
)
|
||||
if (result.success) {
|
||||
this.GdtBalance = Number(result.result.data.gdtSum)
|
||||
this.transactions = result.result.data.transactions
|
||||
@ -129,7 +133,7 @@ export default {
|
||||
this.initScrollbar()
|
||||
},
|
||||
created() {
|
||||
this.updateTransactions()
|
||||
this.updateTransactions({ firstPage: 1, items: 5 })
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<base-nav
|
||||
container-classes="container-fluid"
|
||||
class="navbar-top navbar-expand"
|
||||
class="navbar-expand"
|
||||
:class="{ 'navbar-dark': type === 'default' }"
|
||||
>
|
||||
<!-- Navbar links -->
|
||||
@ -15,7 +15,7 @@
|
||||
<span class="pb-2 text-lg font-weight-bold">
|
||||
{{ $store.state.email }}
|
||||
</span>
|
||||
<b-media-body class="ml-2 d-none d-lg-block d-md-block">
|
||||
<b-media-body class="ml-2">
|
||||
<span class="avatar">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
</span>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-container fluid class="p-lg-2 mt-lg-1">
|
||||
<b-container fluid>
|
||||
<gdd-status
|
||||
v-if="showContext"
|
||||
:pending="pending"
|
||||
@ -31,10 +31,10 @@
|
||||
<gdd-transaction-list
|
||||
v-if="showContext"
|
||||
:transactions="transactions"
|
||||
:max="5"
|
||||
:page-size="5"
|
||||
:timestamp="timestamp"
|
||||
:transactionCount="transactionCount"
|
||||
@update-transactions="$emit('update-transactions')"
|
||||
:transaction-count="transactionCount"
|
||||
@update-transactions="updateTransactions"
|
||||
/>
|
||||
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
|
||||
</b-container>
|
||||
@ -116,6 +116,9 @@ export default {
|
||||
this.transactionData = EMPTY_TRANSACTION_DATA
|
||||
this.currentTransactionStep = 0
|
||||
},
|
||||
updateTransactions(pagination) {
|
||||
this.$emit('update-transactions', pagination)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -202,16 +202,6 @@ describe('GddTransactionList', () => {
|
||||
expect(transaction.findAll('div').at(3).text()).toBe('decay')
|
||||
})
|
||||
})
|
||||
|
||||
describe('max property set to 2', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({ max: 2 })
|
||||
})
|
||||
|
||||
it('shows only 2 transactions', () => {
|
||||
expect(wrapper.findAll('div.gdd-transaction-list-item')).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with invalid transaction type', () => {
|
||||
@ -234,5 +224,69 @@ describe('GddTransactionList', () => {
|
||||
expect(errorHandler).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('pagination buttons', () => {
|
||||
const transactions = Array.from({ length: 42 }, (_, idx) => {
|
||||
return {
|
||||
balance: '3.14',
|
||||
date: '2021-04-29T17:26:40+00:00',
|
||||
memo: 'Kreiszahl PI',
|
||||
name: 'Euklid',
|
||||
transaction_id: idx + 1,
|
||||
type: 'receive',
|
||||
}
|
||||
})
|
||||
|
||||
let paginationButtons
|
||||
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
transactions,
|
||||
transactionCount: 42,
|
||||
showPagination: true,
|
||||
})
|
||||
paginationButtons = wrapper.find('div.pagination-buttons')
|
||||
})
|
||||
|
||||
it('shows the pagination buttons', () => {
|
||||
expect(paginationButtons.exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the previous button disabled', () => {
|
||||
expect(paginationButtons.find('button.previous-page').attributes('disabled')).toBe(
|
||||
'disabled',
|
||||
)
|
||||
})
|
||||
|
||||
it('shows the text "1 / 2"', () => {
|
||||
expect(paginationButtons.find('p.text-center').text()).toBe('1 / 2')
|
||||
})
|
||||
|
||||
it('emits update-transactions when next button is clicked', async () => {
|
||||
paginationButtons.find('button.next-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.emitted('update-transactions')[1]).toEqual([{ firstPage: 2, items: 25 }])
|
||||
})
|
||||
|
||||
it('shows text "2 / 2" when next button is clicked', async () => {
|
||||
paginationButtons.find('button.next-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(paginationButtons.find('p.text-center').text()).toBe('2 / 2')
|
||||
})
|
||||
|
||||
it('has next-button disabled when next button is clicked', async () => {
|
||||
paginationButtons.find('button.next-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(paginationButtons.find('button.next-page').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('emits update-transactions when preivous button is clicked after next buton', async () => {
|
||||
paginationButtons.find('button.next-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
paginationButtons.find('button.previous-page').trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.emitted('update-transactions')[2]).toEqual([{ firstPage: 1, items: 25 }])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="gdd-transaction-list">
|
||||
<b-list-group>
|
||||
<b-list-group-item
|
||||
v-for="item in transactions.slice(0, max)"
|
||||
v-for="item in transactions"
|
||||
:key="item.id"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
@ -66,6 +66,15 @@
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
</b-list-group-item>
|
||||
<pagination-buttons
|
||||
v-if="showPagination && transactionCount > pageSize"
|
||||
:has-next="hasNext"
|
||||
:has-previous="hasPrevious"
|
||||
:total-pages="totalPages"
|
||||
:current-page="currentPage"
|
||||
@show-next="showNext"
|
||||
@show-previous="showPrevious"
|
||||
></pagination-buttons>
|
||||
<div v-if="transactions.length === 0" class="mt-4 text-center">
|
||||
<span>{{ $t('transaction.nullTransactions') }}</span>
|
||||
</div>
|
||||
@ -74,6 +83,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PaginationButtons from '../../../components/PaginationButtons'
|
||||
|
||||
const iconsByType = {
|
||||
send: { icon: 'arrow-left-circle', classes: 'text-danger', operator: '-' },
|
||||
receive: { icon: 'arrow-right-circle', classes: 'gradido-global-color-accent', operator: '+' },
|
||||
@ -83,11 +94,20 @@ const iconsByType = {
|
||||
|
||||
export default {
|
||||
name: 'gdd-transaction-list',
|
||||
components: {
|
||||
PaginationButtons,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentPage: 1,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
transactions: { default: () => [] },
|
||||
max: { type: Number, default: 1000 },
|
||||
pageSize: { type: Number, default: 25 },
|
||||
timestamp: { type: Number, default: 0 },
|
||||
transactionCount: { type: Number, default: 0 },
|
||||
showPagination: { type: Boolean, default: false },
|
||||
},
|
||||
watch: {
|
||||
timestamp: {
|
||||
@ -95,9 +115,23 @@ export default {
|
||||
handler: 'updateTransactions',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasNext() {
|
||||
return this.currentPage * this.pageSize < this.transactionCount
|
||||
},
|
||||
hasPrevious() {
|
||||
return this.currentPage > 1
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.transactionCount / this.pageSize)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateTransactions() {
|
||||
this.$emit('update-transactions')
|
||||
this.$emit('update-transactions', {
|
||||
firstPage: this.currentPage,
|
||||
items: this.pageSize,
|
||||
})
|
||||
},
|
||||
getProperties(item) {
|
||||
const type = iconsByType[item.type]
|
||||
@ -112,6 +146,14 @@ export default {
|
||||
throwError(msg) {
|
||||
throw new Error(msg)
|
||||
},
|
||||
showNext() {
|
||||
this.currentPage++
|
||||
this.updateTransactions()
|
||||
},
|
||||
showPrevious() {
|
||||
this.currentPage--
|
||||
this.updateTransactions()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Header -->
|
||||
<div class="header bg-gradient-info py-7 py-lg-3 pt-lg-2">
|
||||
<b-container>
|
||||
<div class="header-body text-center mb-7">
|
||||
<p class="h1">GDD</p>
|
||||
<p class="h4">Explorer</p>
|
||||
</div>
|
||||
</b-container>
|
||||
<div class="separator separator-bottom separator-skew zindex-100">
|
||||
<svg
|
||||
x="0"
|
||||
y="0"
|
||||
viewBox="0 0 2560 100"
|
||||
preserveAspectRatio="none"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<polygon class="fill-default" points="2560 0 2560 100 0 100"></polygon>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page content -->
|
||||
|
||||
<div>
|
||||
<b-card>
|
||||
<b-card-body>This is some text within a card body.</b-card-body>
|
||||
</b-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<card style="background-color: #ebebeba3 !important">
|
||||
<b-form @submit.prevent="updateProfile">
|
||||
<h6 class="heading-small text-muted mb-4">User information</h6>
|
||||
|
||||
<div class="pl-lg-4">
|
||||
<b-row>
|
||||
<b-col lg="6">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
v-model="user.username"
|
||||
></base-input>
|
||||
</b-col>
|
||||
<b-col lg="6">
|
||||
<base-input
|
||||
type="email"
|
||||
label="Email address"
|
||||
placeholder="mike@email.com"
|
||||
v-model="user.email"
|
||||
></base-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col lg="6">
|
||||
<base-input
|
||||
type="text"
|
||||
label="First Name"
|
||||
placeholder="First Name"
|
||||
v-model="user.firstName"
|
||||
></base-input>
|
||||
</b-col>
|
||||
<b-col lg="6">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Last Name"
|
||||
placeholder="Last Name"
|
||||
v-model="user.lastName"
|
||||
></base-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
<hr class="my-4" />
|
||||
|
||||
<!-- Address -->
|
||||
<h6 class="heading-small text-muted mb-4">Contact information</h6>
|
||||
|
||||
<div class="pl-lg-4">
|
||||
<b-row>
|
||||
<b-col md="12">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Address"
|
||||
placeholder="Home Address"
|
||||
v-model="user.address"
|
||||
></base-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col lg="4">
|
||||
<base-input
|
||||
type="text"
|
||||
label="City"
|
||||
placeholder="City"
|
||||
v-model="user.city"
|
||||
></base-input>
|
||||
</b-col>
|
||||
<b-col lg="4">
|
||||
<base-input
|
||||
type="text"
|
||||
label="Country"
|
||||
placeholder="Country"
|
||||
v-model="user.country"
|
||||
></base-input>
|
||||
</b-col>
|
||||
<b-col lg="4">
|
||||
<base-input
|
||||
label="Postal Code"
|
||||
placeholder="ZIP Code"
|
||||
v-model="user.postalCode"
|
||||
></base-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<hr class="my-4" />
|
||||
<!-- Description -->
|
||||
<h6 class="heading-small text-muted mb-4">About me</h6>
|
||||
<div class="pl-lg-4">
|
||||
<b-form-group
|
||||
label="About Me"
|
||||
label-class="form-control-label"
|
||||
class="mb-0"
|
||||
label-for="about-form-textaria"
|
||||
>
|
||||
<!-- <label class="form-control-label">About Me</label> -->
|
||||
<b-form-textarea
|
||||
rows="4"
|
||||
value="A beautiful premium dashboard for BootstrapVue."
|
||||
id="about-form-textaria"
|
||||
placeholder="A few words about you ..."
|
||||
></b-form-textarea>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</b-form>
|
||||
</card>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
user: {
|
||||
company: 'Creative Code Inc.',
|
||||
username: 'michael23',
|
||||
email: '',
|
||||
firstName: 'Mike',
|
||||
lastName: 'Andrew',
|
||||
address: 'Bld Mihail Kogalniceanu, nr. 8 Bl 1, Sc 1, Ap 09',
|
||||
city: 'New York',
|
||||
country: 'USA',
|
||||
postalCode: '',
|
||||
aboutMe: `Lamborghini Mercy, Your chick she so thirsty, I'm in that two seat Lambo.`,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateProfile() {
|
||||
alert('Your data: ' + JSON.stringify(this.user))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- slot for parent component to activate the file changer -->
|
||||
<div @click="launchFilePicker()">
|
||||
<slot name="activator"></slot>
|
||||
</div>
|
||||
<!-- image input: style is set to hidden and assigned a ref so that it can be triggered -->
|
||||
<input
|
||||
type="file"
|
||||
ref="file"
|
||||
:name="uploadFieldName"
|
||||
@change="onFileChange($event.target.name, $event.target.files)"
|
||||
style="display: none"
|
||||
/>
|
||||
<!-- error dialog displays any potential errors -->
|
||||
<v-dialog v-model="errorDialog" max-width="300">
|
||||
<v-card>
|
||||
<v-card-text class="subheading">{{ errorText }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="errorDialog = false" flat>Got it!</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'avatar-uploader',
|
||||
data: () => ({
|
||||
errorDialog: null,
|
||||
errorText: '',
|
||||
uploadFieldName: 'file',
|
||||
maxSize: 1024,
|
||||
}),
|
||||
props: {
|
||||
// Use "value" here to enable compatibility with v-model
|
||||
value: Object,
|
||||
},
|
||||
methods: {
|
||||
launchFilePicker() {
|
||||
this.$refs.file.click()
|
||||
},
|
||||
onFileChange(fieldName, file) {
|
||||
const { maxSize } = this
|
||||
const imageFile = file[0]
|
||||
|
||||
// check if user actually selected a file
|
||||
if (file.length > 0) {
|
||||
const size = imageFile.size / maxSize / maxSize
|
||||
if (!imageFile.type.match('image.*')) {
|
||||
// check whether the upload is an image
|
||||
this.errorDialog = true
|
||||
this.errorText = 'Please choose an image file'
|
||||
} else if (size > 1) {
|
||||
// check whether the size is greater than the size limit
|
||||
this.errorDialog = true
|
||||
this.errorText = 'Your file is too big! Please select an image under 1MB'
|
||||
} else {
|
||||
// Append file into FormData & turn file into image URL
|
||||
const formData = new FormData()
|
||||
const imageURL = URL.createObjectURL(imageFile)
|
||||
formData.append(fieldName, imageFile)
|
||||
// Emit FormData & image URL to the parent component
|
||||
this.$emit('input', { formData, imageURL })
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -1,67 +1,41 @@
|
||||
<template>
|
||||
<b-card
|
||||
no-body
|
||||
class="card-profile"
|
||||
alt="Image placeholder"
|
||||
img-top
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-row class="justify-content-center">
|
||||
<b-col lg="3" class="order-lg-2">
|
||||
<div class="card-profile-image">
|
||||
<a href="#">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
</a>
|
||||
<b-card class="bg-transparent">
|
||||
<div class="w-100 text-center">
|
||||
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
|
||||
</div>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<div class="card-profile-stats d-flex justify-content-center mt-md-5">
|
||||
<div>
|
||||
<span class="heading">
|
||||
{{ $n(balance) }}
|
||||
</span>
|
||||
<span class="description">GDD</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">{{ transactionCount }}</span>
|
||||
<span class="description">{{ $t('transactions') }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">--</span>
|
||||
<span class="description">{{ $t('community') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-card-header class="text-center border-0 pt-8 pt-md-4 pb-0 pb-md-4">
|
||||
<div class="d-flex justify-content-between">
|
||||
<br />
|
||||
</div>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body class="pt-0">
|
||||
<b-row>
|
||||
<b-col>
|
||||
<div class="card-profile-stats d-flex justify-content-center mt-md-5">
|
||||
<div>
|
||||
<span class="heading">--</span>
|
||||
<span class="description">Transactions</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">--</span>
|
||||
<span class="description">Community</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="heading">
|
||||
{{ $n(balance, 'decimal') }}
|
||||
</span>
|
||||
<span class="description">GDD</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<div class="text-center">
|
||||
<h5 class="h3">
|
||||
{{ this.$store.state.email }}
|
||||
<span class="font-weight-light"></span>
|
||||
</h5>
|
||||
</div>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import VueQrcode from 'vue-qrcode'
|
||||
|
||||
export default {
|
||||
name: 'profilecard',
|
||||
name: 'UserCard',
|
||||
components: {
|
||||
VueQrcode,
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
transactionCount: { type: Number, default: 0 },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
108
frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue
Normal file
108
frontend/src/views/Pages/UserProfile/UserCard_FormUserData.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="userdata_form">
|
||||
<b-card
|
||||
id="userdata_form"
|
||||
class="bg-transparent"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-container>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a href="#userdata_form" v-if="editUserdata" @click="editUserdata = !editUserdata">
|
||||
<span>{{ $t('form.edit') }}</span>
|
||||
</a>
|
||||
<div v-else>
|
||||
<a href="#userdata_form" @click="onSubmit">
|
||||
<span class="mr-4 text-success display-4">{{ $t('form.save') }}</span>
|
||||
</a>
|
||||
<a href="#userdata_form" @click="editUserdata = !editUserdata">
|
||||
<span>
|
||||
<b>{{ $t('form.cancel') }}</b>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<div>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.firstname') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="editUserdata" class="col-md-9 col-sm-10">
|
||||
{{ form.firstName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-input type="text" v-model="form.firstName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-12 col-sm-12 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.lastname') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="editUserdata" class="col-md-9 col-sm-10">
|
||||
{{ form.lastName }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-input type="text" v-model="form.lastName"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.description') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="editUserdata" class="col-md-9 col-sm-10">
|
||||
{{ form.description }}
|
||||
</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-textarea rows="3" max-rows="6" v-model="form.description"></b-textarea>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../../apis/loginAPI'
|
||||
|
||||
export default {
|
||||
name: 'FormUserData',
|
||||
props: {
|
||||
UserProfileTestData: { type: Object },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editUserdata: true,
|
||||
sessionId: this.$store.state.sessionId,
|
||||
form: {
|
||||
firstName: this.$store.state.firstName,
|
||||
lastName: this.$store.state.lastName,
|
||||
description: this.$store.state.description,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
const result = await loginAPI.updateUserInfos(
|
||||
this.$store.state.sessionId,
|
||||
this.$store.state.email,
|
||||
{
|
||||
firstName: this.form.firstName,
|
||||
lastName: this.form.lastName,
|
||||
description: this.form.description,
|
||||
},
|
||||
)
|
||||
if (result.success) {
|
||||
this.$store.commit('firstName', this.form.firstName)
|
||||
this.$store.commit('lastName', this.form.lastName)
|
||||
this.$store.commit('description', this.form.description)
|
||||
this.editUserdata = true
|
||||
} else {
|
||||
alert(result.result.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<b-card id="formusermail" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<b-container>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a href="#formusermail" v-if="edit_email" @click="edit_email = !edit_email">
|
||||
<span>E-Mail {{ $t('form.change') }}</span>
|
||||
</a>
|
||||
<div v-else>
|
||||
<a href="#formusermail" @click="onSubmit">
|
||||
<span class="mr-4 text-success display-4">{{ $t('form.save') }}</span>
|
||||
</a>
|
||||
<a href="#formusermail" @click="edit_email = !edit_email">
|
||||
<span>
|
||||
<b>{{ $t('form.cancel') }}</b>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>E-Mail</small>
|
||||
</b-col>
|
||||
<b-col v-if="edit_email" class="col-md-9 col-sm-10">{{ $store.state.email }}</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<b-input type="text" v-model="newEmail"></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../../apis/loginAPI'
|
||||
|
||||
export default {
|
||||
name: 'FormUserMail',
|
||||
data() {
|
||||
return {
|
||||
edit_email: true,
|
||||
newEmail: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
// console.log(this.data)
|
||||
const result = await loginAPI.changeEmailProfil(
|
||||
this.$store.state.sessionId,
|
||||
this.email,
|
||||
this.newEmail,
|
||||
)
|
||||
if (result.success) {
|
||||
alert('changePassword success')
|
||||
} else {
|
||||
alert(result.result.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<b-card id="change_pwd" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<b-container>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a href="#change_pwd" v-if="edit_pwd" @click="edit_pwd = !edit_pwd">
|
||||
<span>{{ $t('form.password') }} {{ $t('form.change') }}</span>
|
||||
</a>
|
||||
<div v-else>
|
||||
<a href="#change_pwd" @click="onSubmit">
|
||||
<span class="mr-4 text-success display-4">{{ $t('form.save') }}</span>
|
||||
</a>
|
||||
<a href="#change_pwd" @click="edit_pwd = !edit_pwd">
|
||||
<span>
|
||||
<b>{{ $t('form.cancel') }}</b>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<div v-if="!edit_pwd">
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.password_old') }}</small>
|
||||
</b-col>
|
||||
<b-col class="col-md-9 col-sm-10">
|
||||
<b-input
|
||||
type="text"
|
||||
:placeholder="$t('form.password_old')"
|
||||
v-model="password"
|
||||
></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.password_new') }}</small>
|
||||
</b-col>
|
||||
<b-col class="col-md-9 col-sm-10">
|
||||
<b-input
|
||||
type="text"
|
||||
:placeholder="$t('form.password_new')"
|
||||
v-model="passwordNew"
|
||||
></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.password_new_repeat') }}</small>
|
||||
</b-col>
|
||||
<b-col class="col-md-9 col-sm-10">
|
||||
<b-input
|
||||
type="text"
|
||||
:placeholder="$t('form.password_new_repeat')"
|
||||
v-model="passwordNew2"
|
||||
></b-input>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../../apis/loginAPI'
|
||||
|
||||
export default {
|
||||
name: 'FormUserPasswort',
|
||||
data() {
|
||||
return {
|
||||
edit_pwd: true,
|
||||
email: null,
|
||||
password: '',
|
||||
passwordNew: '',
|
||||
passwordNew2: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
// console.log(this.data)
|
||||
const result = await loginAPI.changePasswordProfile(
|
||||
this.$store.state.sessionId,
|
||||
this.email,
|
||||
this.password,
|
||||
this.passwordNew,
|
||||
)
|
||||
if (result.success) {
|
||||
alert('changePassword success')
|
||||
} else {
|
||||
alert(result.result.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<b-card id="formusername" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<b-container>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a href="#formusername" v-if="edit_username" @click="edit_username = !edit_username">
|
||||
<span>{{ $t('form.username') }} {{ $t('form.change') }}</span>
|
||||
</a>
|
||||
<div v-else>
|
||||
<a href="#formusername" @click="edit_username = !edit_username">
|
||||
<span>
|
||||
<b>{{ $t('form.cancel') }}</b>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-lg-3 col-md-10 col-sm-10 text-md-left text-lg-right">
|
||||
<small>{{ $t('form.username') }}</small>
|
||||
</b-col>
|
||||
<b-col v-if="edit_username" class="col-md-9 col-sm-10">@{{ $store.state.username }}</b-col>
|
||||
<b-col v-else class="col-md-9 col-sm-10">
|
||||
<validation-observer v-slot="{ handleSubmit }" ref="formValidator">
|
||||
<b-form role="form" @submit.prevent="handleSubmit(onSubmit)">
|
||||
<b-form-input v-model="username" :placeholder="$store.state.username"></b-form-input>
|
||||
<div>
|
||||
{{ $t('form.change_username_info') }}
|
||||
</div>
|
||||
<div class="text-center" ref="submitButton">
|
||||
<b-button type="submit" class="mt-4">
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-form>
|
||||
</validation-observer>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import loginAPI from '../../../apis/loginAPI'
|
||||
|
||||
export default {
|
||||
name: 'FormUsername',
|
||||
data() {
|
||||
return {
|
||||
edit_username: true,
|
||||
username: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
// console.log(this.data)
|
||||
const result = await loginAPI.changeUsernameProfile(this.username)
|
||||
if (result.success) {
|
||||
alert('changeUsername success')
|
||||
} else {
|
||||
alert(result.result.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -1,71 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="header pb-7 pt-5"></div>
|
||||
|
||||
<b-container fluid class="mt--6">
|
||||
<div class="display-4 mb-3">{{ $t('site.profil.activity.chart') }}</div>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<div class="chart">
|
||||
<line-chart :height="350" :chart-data="bigLineChart.chartData"></line-chart>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<hr />
|
||||
<div class="display-4 mt-6">{{ $t('site.profil.activity.new') }}</div>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<gdd-add-work-2 />
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<hr />
|
||||
<div class="display-4 mb-3">{{ $t('site.profil.activity.list') }}</div>
|
||||
<b-row>
|
||||
<b-col class="mb-5">
|
||||
{{ $t('community') }}
|
||||
<gdd-work-table></gdd-work-table>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import GddWorkTable from '../../views/Pages/AccountOverview/GddWorkTable.vue'
|
||||
import GddAddWork2 from '../../views/Pages/AccountOverview/GddAddWork2.vue'
|
||||
|
||||
import * as chartConfigs from '@/components/Charts/config'
|
||||
import LineChart from '@/components/Charts/LineChart'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GddWorkTable,
|
||||
LineChart,
|
||||
GddAddWork2,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bigLineChart: {
|
||||
allData: [
|
||||
[0, 20, 10, 30, 15, 40, 20, 60, 60],
|
||||
[0, 20, 5, 25, 10, 30, 35, 60, 40],
|
||||
[0, 2, 5, 7, 10, 30, 15, 9, 10],
|
||||
[0, 2, 5, 7, 10, 14, 29, 78, 120],
|
||||
],
|
||||
activeIndex: 0,
|
||||
chartData: {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Gemeinschaftsstunden',
|
||||
data: [30, 20, 10, 30, 65, 40, 20, 60, 70],
|
||||
},
|
||||
],
|
||||
labels: ['2020 Aug', 'Sep', 'Okt', 'Nov', 'Dez', 'Jan', 'Feb', 'Mär 2021'],
|
||||
},
|
||||
extraOptions: chartConfigs.blueChartOptions,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -1,23 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="header pb-8 pt-5 pt-lg-8 d-flex align-items-center profile-header"></div>
|
||||
|
||||
<b-container fluid class="mt--6">
|
||||
<b-row>
|
||||
<b-col xl="12" class="order-xl-1">
|
||||
<edit-profile-form></edit-profile-form>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import EditProfileForm from './UserProfile/EditProfileForm.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditProfileForm,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
29
frontend/src/views/Pages/UserProfileOverview.vue
Normal file
29
frontend/src/views/Pages/UserProfileOverview.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<b-container fluid>
|
||||
<user-card :balance="balance" :transactionCount="transactionCount"></user-card>
|
||||
<form-user-data :UserProfileTestData="UserProfileTestData" />
|
||||
<form-username />
|
||||
<form-user-passwort />
|
||||
</b-container>
|
||||
</template>
|
||||
<script>
|
||||
import UserCard from './UserProfile/UserCard.vue'
|
||||
import FormUserData from './UserProfile/UserCard_FormUserData.vue'
|
||||
import FormUsername from './UserProfile/UserCard_FormUsername.vue'
|
||||
import FormUserPasswort from './UserProfile/UserCard_FormUserPasswort.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserCard,
|
||||
FormUserData,
|
||||
FormUsername,
|
||||
FormUserPasswort,
|
||||
},
|
||||
props: {
|
||||
balance: { type: Number, default: 0 },
|
||||
transactionCount: { type: Number, default: 0 },
|
||||
UserProfileTestData: { type: Object },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
@ -1,16 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="header pb-sm-1 pb-md-7 d-flex align-items-center profile-header"
|
||||
style="max-height: 200px"
|
||||
></div>
|
||||
|
||||
<gdd-transaction-list
|
||||
:timestamp="timestamp"
|
||||
:transactionCount="transactionCount"
|
||||
:transactions="transactions"
|
||||
@update-transactions="updateTransactions"
|
||||
/>
|
||||
<b-container fluid>
|
||||
<b-row>
|
||||
<b-col class="order-xl-1">
|
||||
<gdd-transaction-list
|
||||
:timestamp="timestamp"
|
||||
:transactionCount="transactionCount"
|
||||
:transactions="transactions"
|
||||
:show-pagination="true"
|
||||
@update-transactions="updateTransactions"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -32,8 +34,8 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateTransactions() {
|
||||
this.$emit('update-transactions')
|
||||
updateTransactions(pagination) {
|
||||
this.$emit('update-transactions', pagination)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -170,6 +170,7 @@ FILE(GLOB TEST_CRYPTO "src/cpp/test/crypto/*.cpp" "src/cpp/test/crypto/*.h")
|
||||
FILE(GLOB TEST_MODEL "src/cpp/test/model/*.cpp" "src/cpp/test/model/*.h")
|
||||
FILE(GLOB TEST_MODEL_TABLE "src/cpp/test/model/table/*.cpp" "src/cpp/test/model/table/*.h")
|
||||
FILE(GLOB TEST_CONTROLLER "src/cpp/test/controller/*.cpp" "src/cpp/test/controller/*.h")
|
||||
FILE(GLOB TEST_JSON_INTERFACE "src/cpp/test/JSONInterface/*.cpp" "src/cpp/test/JSONInterface/*.h")
|
||||
|
||||
SET(LOCAL_SRCS
|
||||
${CONTROLLER} ${TINF} ${MAIN} ${HTTPInterface} ${COMPILED_PAGES}
|
||||
@ -179,7 +180,7 @@ SET(LOCAL_SRCS
|
||||
${PROTO_GRADIDO}
|
||||
)
|
||||
SET(LOCAL_TEST_SRC
|
||||
${TEST} ${TEST_CRYPTO} ${TEST_MODEL} ${TEST_MODEL_TABLE} ${TEST_CONTROLLER}
|
||||
${TEST} ${TEST_CRYPTO} ${TEST_MODEL} ${TEST_MODEL_TABLE} ${TEST_CONTROLLER} ${TEST_JSON_INTERFACE}
|
||||
)
|
||||
aux_source_directory("src/cpp" LOCAL_SRCS)
|
||||
|
||||
@ -204,6 +205,7 @@ if(MSVC)
|
||||
source_group("Test\\model\\table" FILES ${TEST_MODEL_TABLE})
|
||||
source_group("Test\\model" FILES ${TEST_MODEL})
|
||||
source_group("Test\\controller" FILES ${TEST_CONTROLLER})
|
||||
source_group("Test\\Json-Interface" FILES ${TEST_JSON_INTERFACE})
|
||||
source_group("Test" FILES ${TEST})
|
||||
endif()
|
||||
|
||||
@ -341,7 +343,7 @@ target_compile_definitions(Gradido_LoginServer_Test PUBLIC "_TEST_BUILD")
|
||||
target_link_libraries(Gradido_LoginServer_Test ${GRPC_LIBS} )
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(Gradido_LoginServer_Test ${CONAN_LIBS} )
|
||||
target_link_libraries(Gradido_LoginServer_Test ${CONAN_LIBS} libmariadb libprotobuf)
|
||||
#TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test optimized ${MYSQL_LIBRARIES} Shlwapi)
|
||||
#TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test debug ${COMPILED_MARIADB_CLIENT_DEBUG} Shlwapi)
|
||||
#TARGET_LINK_LIBRARIES(Gradido_LoginServer_Test debug ${GRPC_LIBS} ${PROTOBUF_DEBUG_LIBS})
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
Poco/1.9.4@pocoproject/stable
|
||||
libsodium/1.0.18@bincrafters/stable
|
||||
boost/1.71.0@conan/stable
|
||||
gtest/1.10.0
|
||||
|
||||
[options]
|
||||
Poco:enable_pagecompiler=True
|
||||
|
||||
92
login_server/src/cpp/JSONInterface/JsonCheckUsername.cpp
Normal file
92
login_server/src/cpp/JSONInterface/JsonCheckUsername.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "JsonCheckUsername.h"
|
||||
#include "Poco/URI.h"
|
||||
#include "controller/User.h"
|
||||
#include "lib/DataTypeConverter.h"
|
||||
|
||||
Poco::JSON::Object* JsonCheckUsername::handle(Poco::Dynamic::Var params)
|
||||
{
|
||||
std::string username;
|
||||
int group_id = 0;
|
||||
std::string group_alias;
|
||||
|
||||
// if is json object
|
||||
if (params.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
Poco::JSON::Object::Ptr paramJsonObject = params.extract<Poco::JSON::Object::Ptr>();
|
||||
/// Throws a RangeException if the value does not fit
|
||||
/// into the result variable.
|
||||
/// Throws a NotImplementedException if conversion is
|
||||
/// not available for the given type.
|
||||
/// Throws InvalidAccessException if Var is empty.
|
||||
|
||||
auto username_obj = paramJsonObject->get("username");
|
||||
auto group_id_obj = paramJsonObject->get("group_id");
|
||||
auto group_alias_obj = paramJsonObject->get("group_alias");
|
||||
|
||||
try {
|
||||
|
||||
if (!username_obj.isEmpty()) {
|
||||
username_obj.convert(username);
|
||||
}
|
||||
|
||||
if (!group_id_obj.isEmpty()) {
|
||||
group_id_obj.convert(group_id);
|
||||
}
|
||||
if (!group_alias_obj.isEmpty()) {
|
||||
group_alias_obj.convert(group_alias);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
return stateError("Poco Exception", ex.displayText());
|
||||
}
|
||||
|
||||
}
|
||||
else if (params.isVector()) {
|
||||
const Poco::URI::QueryParameters queryParams = params.extract<Poco::URI::QueryParameters>();
|
||||
for (auto it = queryParams.begin(); it != queryParams.end(); it++) {
|
||||
if (it->first == "username") {
|
||||
username = it->second;
|
||||
}
|
||||
else if (it->first == "group_id") {
|
||||
DataTypeConverter::strToInt(it->second, group_id);
|
||||
}
|
||||
else if (it->first == "group_alias") {
|
||||
group_alias = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return stateError("format not implemented", std::string(params.type().name()));
|
||||
}
|
||||
|
||||
if (!group_id && group_alias == "") {
|
||||
return stateError("no group given");
|
||||
}
|
||||
if (!group_id) {
|
||||
auto groups = controller::Group::load(group_alias);
|
||||
if (groups.size() > 1) {
|
||||
return stateError("group is ambiguous");
|
||||
}
|
||||
if (!groups.size()) {
|
||||
return stateError("unknown group");
|
||||
}
|
||||
group_id = groups[0]->getModel()->getID();
|
||||
}
|
||||
auto group = controller::Group::load(group_id);
|
||||
if (group.isNull()) {
|
||||
return stateError("unknown group");
|
||||
}
|
||||
auto user = controller::User::create();
|
||||
user->getModel()->setGroupId(group_id);
|
||||
if (username == "") {
|
||||
Poco::JSON::Object* result = new Poco::JSON::Object;
|
||||
result->set("state", "success");
|
||||
result->set("group_id", group_id);
|
||||
return result;
|
||||
}
|
||||
if (user->isUsernameAlreadyUsed(username)) {
|
||||
return stateWarning("username already in use");
|
||||
}
|
||||
return stateSuccess();
|
||||
|
||||
}
|
||||
16
login_server/src/cpp/JSONInterface/JsonCheckUsername.h
Normal file
16
login_server/src/cpp/JSONInterface/JsonCheckUsername.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __JSON_INTERFACE_JSON_CHECK_USERNAME_
|
||||
#define __JSON_INTERFACE_JSON_CHECK_USERNAME_
|
||||
|
||||
#include "JsonRequestHandler.h"
|
||||
|
||||
class JsonCheckUsername : public JsonRequestHandler
|
||||
{
|
||||
public:
|
||||
Poco::JSON::Object* handle(Poco::Dynamic::Var params);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // __JSON_INTERFACE_JSON_CHECK_USERNAME_
|
||||
@ -135,6 +135,7 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
|
||||
else {
|
||||
printf("user hasn't valid key pair set\n");
|
||||
}
|
||||
Poco::JSON::Array* json_warnings = nullptr;
|
||||
if (!result) {
|
||||
try {
|
||||
auto transaction = model::gradido::Transaction::createTransfer(sender_user, target_pubkey, mTargetGroup, amount, mMemo, mBlockchainType);
|
||||
@ -149,6 +150,10 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
|
||||
if (errors.size() > 0) {
|
||||
return stateError("error by signing transaction", errors);
|
||||
}
|
||||
if (transaction->warningCount() > 0) {
|
||||
json_warnings = new Poco::JSON::Array;
|
||||
json_warnings->add(transaction->getWarningsArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
@ -164,6 +169,10 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
|
||||
return stateError("exception");
|
||||
}
|
||||
result = stateSuccess();
|
||||
if (json_warnings) {
|
||||
result->set("warnings", json_warnings);
|
||||
delete json_warnings;
|
||||
}
|
||||
}
|
||||
mm->releaseMemory(target_pubkey);
|
||||
return result;
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
#include "JsonAdminEmailVerificationResend.h"
|
||||
#include "JsonCheckSessionState.h"
|
||||
#include "JsonCheckUsername.h"
|
||||
#include "JsonAppLogin.h"
|
||||
#include "JsonAquireAccessToken.h"
|
||||
#include "JsonCreateTransaction.h"
|
||||
@ -74,6 +75,9 @@ Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(c
|
||||
else if (url_first_part == "/checkSessionState") {
|
||||
return new JsonCheckSessionState;
|
||||
}
|
||||
else if (url_first_part == "/checkUsername") {
|
||||
return new JsonCheckUsername;
|
||||
}
|
||||
else if (url_first_part == "/createTransaction") {
|
||||
return new JsonCreateTransaction;
|
||||
}
|
||||
|
||||
@ -58,31 +58,37 @@ Poco::JSON::Object* JsonUnsecureLogin::handle(Poco::Dynamic::Var params)
|
||||
}
|
||||
|
||||
auto user = controller::User::create();
|
||||
std::string message;
|
||||
std::string details;
|
||||
if (email.size()) {
|
||||
if (!sm->isValid(email, VALIDATE_EMAIL)) {
|
||||
return stateError("invalid email");
|
||||
message = "invalid email";
|
||||
}
|
||||
if (1 != user->load(email)) {
|
||||
return stateError("user with email not found", email);
|
||||
message = "user with email not found";
|
||||
details = email;
|
||||
}
|
||||
}
|
||||
else if (username.size() > 0) {
|
||||
if (1 != user->load(username)) {
|
||||
return stateError("user with username not found", username);
|
||||
message = "user with username not found";
|
||||
details = username;
|
||||
}
|
||||
email = user->getModel()->getEmail();
|
||||
}
|
||||
if (message.size()) {
|
||||
Poco::Thread::sleep(ServerConfig::g_FakeLoginSleepTime);
|
||||
return stateError(message.data(), details);
|
||||
}
|
||||
|
||||
NotificationList pwd_errors;
|
||||
Poco::JSON::Object* result = new Poco::JSON::Object;
|
||||
|
||||
if (!password.size() || !sm->checkPwdValidation(password, &pwd_errors, LanguageManager::getInstance()->getFreeCatalog(LANG_EN))) {
|
||||
|
||||
Poco::Thread::sleep(ServerConfig::g_FakeLoginSleepTime);
|
||||
result->set("state", "error");
|
||||
result->set("msg", pwd_errors.getLastError()->getString(false));
|
||||
if (pwd_errors.errorCount()) {
|
||||
result->set("details", pwd_errors.getLastError()->getString(false));
|
||||
}
|
||||
result->set("msg", "password incorrect");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -108,7 +108,9 @@ namespace controller {
|
||||
bool User::isUsernameAlreadyUsed(const std::string& username)
|
||||
{
|
||||
auto db = getModel();
|
||||
return db->loadFromDB({ "username", "group_id" }, username, db->getGroupId(), model::table::MYSQL_CONDITION_AND) > 0;
|
||||
auto results = db->loadMultipleFromDB<model::table::UserTuple>({ "username", "group_id" }, username, db->getGroupId(), model::table::MYSQL_CONDITION_AND);
|
||||
return results.size() > 0;
|
||||
|
||||
}
|
||||
|
||||
int User::load(const unsigned char* pubkey_array)
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "sodium.h"
|
||||
#include "../SingletonManager/MemoryManager.h"
|
||||
#include "DataTypeConverter.h"
|
||||
#include "Warning.h"
|
||||
|
||||
JsonRequest::JsonRequest(const std::string& serverHost, int serverPort)
|
||||
: mServerHost(serverHost), mServerPort(serverPort)
|
||||
@ -125,11 +126,19 @@ JsonRequestReturn JsonRequest::request(const char* methodName, const Poco::JSON:
|
||||
return JSON_REQUEST_RETURN_ERROR;
|
||||
}
|
||||
else if (stateString == "success") {
|
||||
auto warnings_obj = object.get("warnings");
|
||||
if (!warnings_obj.isEmpty()) {
|
||||
Poco::JSON::Object warnings = *parsedJson.extract<Poco::JSON::Object::Ptr>();
|
||||
for (auto it = warnings.begin(); it != warnings.end(); it++) {
|
||||
addWarning(new Warning(it->first, it->second.toString()));
|
||||
}
|
||||
}
|
||||
for (auto it = object.begin(); it != object.end(); it++) {
|
||||
if (it->first == "state") continue;
|
||||
std::string index = it->first;
|
||||
std::string value = it->second.toString();
|
||||
printf("[JsonRequest] %s: %s\n", index.data(), value.data());
|
||||
|
||||
//printf("[JsonRequest] %s: %s\n", index.data(), value.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,95 +174,5 @@ JsonRequestReturn JsonRequest::request(const char* methodName)
|
||||
return request(methodName, requestJson);
|
||||
}
|
||||
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
JsonRequestReturn JsonRequest::requestGRPCRelay(const Poco::Net::NameValueCollection& payload)
|
||||
{
|
||||
static const char* functionName = "JsonRequest::requestGRPCRelay";
|
||||
Poco::JSON::Object requestJson;
|
||||
|
||||
for (auto it = payload.begin(); it != payload.end(); it++) {
|
||||
requestJson.set(it->first, it->second);
|
||||
}
|
||||
|
||||
// send post request via https
|
||||
// 443 = HTTPS Default
|
||||
// TODO: adding port into ServerConfig
|
||||
try {
|
||||
Profiler phpRequestTime;
|
||||
Poco::Net::HTTPClientSession httpClientSession(mServerHost, mServerPort);
|
||||
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, "/hedera_rpc_relay/gRPCProxy.php");
|
||||
|
||||
request.setChunkedTransferEncoding(false);
|
||||
std::ostream& requestStream = httpClientSession.sendRequest(request);
|
||||
requestJson.stringify(requestStream);
|
||||
|
||||
std::stringstream ss;
|
||||
requestJson.stringify(ss);
|
||||
auto f = fopen("grpc.txt", "wt");
|
||||
std::string grpc = ss.str();
|
||||
fwrite(grpc.data(), grpc.size(), 1, f);
|
||||
fclose(f);
|
||||
|
||||
Poco::Net::HTTPResponse response;
|
||||
std::istream& request_stream = httpClientSession.receiveResponse(response);
|
||||
|
||||
// debugging answer
|
||||
|
||||
std::stringstream responseStringStream;
|
||||
for (std::string line; std::getline(request_stream, line); ) {
|
||||
responseStringStream << line << std::endl;
|
||||
}
|
||||
Poco::Logger& speedLog = Poco::Logger::get("SpeedLog");
|
||||
speedLog.information("[gRPC relay] php server time: %s", phpRequestTime.string());
|
||||
|
||||
// extract parameter from request
|
||||
Poco::JSON::Parser jsonParser;
|
||||
Poco::Dynamic::Var parsedJson;
|
||||
try {
|
||||
parsedJson = jsonParser.parse(responseStringStream.str());
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
addError(new ParamError(functionName, "error parsing request answer grpc relay", ex.displayText().data()));
|
||||
|
||||
std::string fileName = "response_grpc_";
|
||||
fileName += ".html";
|
||||
|
||||
FILE* f = fopen(fileName.data(), "wt");
|
||||
std::string responseString = responseStringStream.str();
|
||||
fwrite(responseString.data(), 1, responseString.size(), f);
|
||||
fclose(f);
|
||||
// */
|
||||
sendErrorsAsEmail(responseStringStream.str());
|
||||
return JSON_REQUEST_RETURN_PARSE_ERROR;
|
||||
}
|
||||
|
||||
Poco::JSON::Object object = *parsedJson.extract<Poco::JSON::Object::Ptr>();
|
||||
auto state = object.get("state");
|
||||
std::string stateString = state.convert<std::string>();
|
||||
if (stateString == "error") {
|
||||
addError(new Error(functionName, "php server return error"));
|
||||
if (!object.isNull("msg")) {
|
||||
addError(new ParamError(functionName, "msg:", object.get("msg").convert<std::string>().data()));
|
||||
}
|
||||
if (!object.isNull("details")) {
|
||||
addError(new ParamError(functionName, "details:", object.get("details").convert<std::string>().data()));
|
||||
}
|
||||
// send copy of errors as email, to have result also in db
|
||||
sendErrorsAsEmail("", true);
|
||||
return JSON_REQUEST_RETURN_ERROR;
|
||||
}
|
||||
ss.clear();
|
||||
Poco::JSON::Stringifier::stringify(object, ss);
|
||||
printf("json request result: %s\n", ss.str().data());
|
||||
}
|
||||
catch (Poco::Exception& e) {
|
||||
addError(new ParamError(functionName, "connect error to php server", e.displayText().data()));
|
||||
sendErrorsAsEmail();
|
||||
return JSON_REQUEST_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return JSON_REQUEST_RETURN_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ public:
|
||||
JsonRequestReturn request(const char* methodName, const Poco::Net::NameValueCollection& payload);
|
||||
JsonRequestReturn request(const char* methodName, const Poco::JSON::Object& payload);
|
||||
JsonRequestReturn request(const char* methodName);
|
||||
JsonRequestReturn requestGRPCRelay(const Poco::Net::NameValueCollection& payload);
|
||||
|
||||
protected:
|
||||
int mServerPort;
|
||||
|
||||
@ -10,4 +10,10 @@ Notification::Notification(const char* functionName, const std::string& message)
|
||||
: mFunctionName(functionName), mMessage(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Notification::Notification(const std::string& functionName, const std::string& message)
|
||||
: mFunctionName(functionName), mMessage(message)
|
||||
{
|
||||
|
||||
}
|
||||
@ -8,6 +8,7 @@ class Notification
|
||||
public:
|
||||
Notification(const char* functionName, const char* message);
|
||||
Notification(const char* functionName, const std::string& message);
|
||||
Notification(const std::string& functionName, const std::string& message);
|
||||
|
||||
const char* getFunctionName() { return mFunctionName.data(); }
|
||||
const char* getMessage() { return mMessage.data(); }
|
||||
@ -16,6 +17,7 @@ public:
|
||||
|
||||
virtual bool isError() { return false; }
|
||||
virtual bool isSuccess() { return false; }
|
||||
virtual bool isWarning() { return false; }
|
||||
|
||||
protected:
|
||||
std::string mFunctionName;
|
||||
|
||||
@ -54,6 +54,11 @@ NotificationList::~NotificationList()
|
||||
delete mErrorStack.top();
|
||||
mErrorStack.pop();
|
||||
}
|
||||
|
||||
while (mWarningStack.size() > 0) {
|
||||
delete mWarningStack.top();
|
||||
mWarningStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationList::addError(Notification* error, bool log/* = true */)
|
||||
@ -61,12 +66,21 @@ void NotificationList::addError(Notification* error, bool log/* = true */)
|
||||
|
||||
if (log) {
|
||||
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
|
||||
mLogging.error("%s [ErrorList::addError] %s", dateTimeString, error->getString(false));
|
||||
mLogging.error("%s [NotificationList::addError] %s", dateTimeString, error->getString(false));
|
||||
|
||||
}
|
||||
mErrorStack.push(error);
|
||||
}
|
||||
|
||||
void NotificationList::addWarning(Warning* warning, bool log/* = true*/)
|
||||
{
|
||||
if (log) {
|
||||
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
|
||||
mLogging.warning("%s [NotificationList::addWarning] %s", dateTimeString, warning->getString(false));
|
||||
}
|
||||
mWarningStack.push(warning);
|
||||
}
|
||||
|
||||
void NotificationList::addNotification(Notification* notification)
|
||||
{
|
||||
mErrorStack.push(notification);
|
||||
@ -86,6 +100,20 @@ Notification* NotificationList::getLastError()
|
||||
return error;
|
||||
}
|
||||
|
||||
Warning* NotificationList::getLastWarning()
|
||||
{
|
||||
if (mWarningStack.size() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Warning* warning = mWarningStack.top();
|
||||
if (warning) {
|
||||
mWarningStack.pop();
|
||||
}
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
void NotificationList::clearErrors()
|
||||
{
|
||||
while (mErrorStack.size()) {
|
||||
@ -109,6 +137,17 @@ int NotificationList::getErrors(NotificationList* send)
|
||||
return iCount;
|
||||
}
|
||||
|
||||
int NotificationList::getWarnings(NotificationList* send)
|
||||
{
|
||||
Warning* warning = nullptr;
|
||||
int iCount = 0;
|
||||
while (warning = send->getLastWarning()) {
|
||||
addWarning(warning, false);
|
||||
iCount++;
|
||||
}
|
||||
return iCount;
|
||||
}
|
||||
|
||||
void NotificationList::printErrors()
|
||||
{
|
||||
while (mErrorStack.size() > 0) {
|
||||
@ -134,6 +173,21 @@ std::vector<std::string> NotificationList::getErrorsArray()
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> NotificationList::getWarningsArray()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve(mWarningStack.size());
|
||||
|
||||
while (mWarningStack.size() > 0) {
|
||||
auto warning = mWarningStack.top();
|
||||
mWarningStack.pop();
|
||||
//result->add(error->getString());
|
||||
result.push_back(warning->getString());
|
||||
delete warning;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string NotificationList::getErrorsHtml()
|
||||
{
|
||||
std::string res;
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#define DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H
|
||||
|
||||
#include "Error.h"
|
||||
#include "Warning.h"
|
||||
#include <stack>
|
||||
|
||||
#include "../tasks/CPUTask.h"
|
||||
@ -28,11 +29,14 @@ public:
|
||||
// push error, error will be deleted in deconstructor
|
||||
virtual void addError(Notification* error, bool log = true);
|
||||
void addNotification(Notification* notification);
|
||||
virtual void addWarning(Warning* warning, bool log = true);
|
||||
|
||||
// return error on top of stack, please delete after using
|
||||
Notification* getLastError();
|
||||
Warning* getLastWarning();
|
||||
|
||||
inline size_t errorCount() { return mErrorStack.size(); }
|
||||
inline size_t warningCount() { return mWarningStack.size(); }
|
||||
|
||||
// delete all errors
|
||||
void clearErrors();
|
||||
@ -41,16 +45,19 @@ public:
|
||||
return recv->getErrors(send);
|
||||
}
|
||||
int getErrors(NotificationList* send);
|
||||
int getWarnings(NotificationList* send);
|
||||
|
||||
void printErrors();
|
||||
std::string getErrorsHtml();
|
||||
std::string getErrorsHtmlNewFormat();
|
||||
std::vector<std::string> getErrorsArray();
|
||||
std::vector<std::string> getWarningsArray();
|
||||
|
||||
void sendErrorsAsEmail(std::string rawHtml = "", bool copy = false);
|
||||
|
||||
protected:
|
||||
std::stack<Notification*> mErrorStack;
|
||||
std::stack<Warning*> mWarningStack;
|
||||
// poco logging
|
||||
Poco::Logger& mLogging;
|
||||
};
|
||||
|
||||
58
login_server/src/cpp/lib/Warning.cpp
Normal file
58
login_server/src/cpp/lib/Warning.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "Warning.h"
|
||||
#include <sstream>
|
||||
|
||||
Warning::Warning(const char* functionName, const char* message)
|
||||
: Notification(functionName, message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Warning::Warning(const std::string& functionName, const std::string& message)
|
||||
: Notification(functionName, message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string Warning::getString(bool withNewline/* = true*/) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << mFunctionName << ": " << mMessage;
|
||||
if (withNewline) ss << std::endl;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
std::string Warning::getHtmlString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << mFunctionName << ": " << mMessage;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
ParamWarning::ParamWarning(const char* functionName, const char* message, std::string param)
|
||||
: Warning(functionName, message), mParam(param)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParamWarning::ParamWarning(const char* functionName, const char* message, int param)
|
||||
: Warning(functionName, message), mParam(std::to_string(param))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string ParamWarning::getString(bool withNewline/* = true*/) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << mFunctionName << ": " << mMessage << " " << mParam;
|
||||
if (withNewline) ss << std::endl;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
std::string ParamWarning::getHtmlString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << mFunctionName << ": " << mMessage << " " << mParam;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
31
login_server/src/cpp/lib/Warning.h
Normal file
31
login_server/src/cpp/lib/Warning.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef GRADIDO_LOGIN_SERVER_LIB_WARNING_H
|
||||
#define GRADIDO_LOGIN_SERVER_LIB_WARNING_H
|
||||
|
||||
#include "Notification.h"
|
||||
|
||||
class Warning : public Notification
|
||||
{
|
||||
public:
|
||||
Warning(const char* functionName, const char* message);
|
||||
Warning(const std::string& functionName, const std::string& message);
|
||||
|
||||
std::string getString(bool withNewline = true) const;
|
||||
std::string getHtmlString() const;
|
||||
|
||||
virtual bool isWarning() { return true; }
|
||||
};
|
||||
|
||||
class ParamWarning : public Warning
|
||||
{
|
||||
public:
|
||||
ParamWarning(const char* functionName, const char* message, std::string param);
|
||||
ParamWarning(const char* functionName, const char* message, int param);
|
||||
|
||||
std::string getString(bool withNewline = true) const;
|
||||
std::string getHtmlString() const;
|
||||
|
||||
protected:
|
||||
std::string mParam;
|
||||
};
|
||||
|
||||
#endif //GRADIDO_LOGIN_SERVER_LIB_WARNING_H
|
||||
@ -561,17 +561,21 @@ namespace model {
|
||||
Poco::Net::NameValueCollection param;
|
||||
param.set("transaction", base_64_message);
|
||||
auto result = json_request.request("putTransaction", param);
|
||||
if (JSON_REQUEST_RETURN_OK == result) {
|
||||
json_request.getWarnings(&json_request);
|
||||
|
||||
if (JSON_REQUEST_RETURN_OK == result)
|
||||
{
|
||||
if (!json_request.errorCount()) {
|
||||
finishSuccess();
|
||||
}
|
||||
else {
|
||||
getErrors(&json_request);
|
||||
getErrors(&json_request);
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
json_request.getWarnings(&json_request);
|
||||
getErrors(&json_request);
|
||||
|
||||
return -1;
|
||||
|
||||
@ -61,6 +61,12 @@ namespace model {
|
||||
template<class T1, class T2>
|
||||
size_t loadFromDB(const std::vector<std::string>& fieldNames, const T1& field1Value, const T2& field2Value, MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
|
||||
template<class Tuple, class T1, class T2>
|
||||
std::vector<Tuple> loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
const T1& field1Value, const T2& field2Value,
|
||||
MysqlConditionType conditionType = MYSQL_CONDITION_AND);
|
||||
|
||||
template<class Tuple, class T1, class T2, class T3, class T4>
|
||||
std::vector<Tuple> loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
@ -290,6 +296,43 @@ namespace model {
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
template<class Tuple, class T1, class T2>
|
||||
std::vector<Tuple> ModelBase::loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
const T1& field1Value, const T2& field2Value,
|
||||
MysqlConditionType conditionType/* = MYSQL_CONDITION_AND*/)
|
||||
{
|
||||
auto cm = ConnectionManager::getInstance();
|
||||
std::vector<Tuple> results;
|
||||
if (fieldNames.size() != 2) {
|
||||
addError(new Error(getTableName(), "error in loadFromDB with 2 different field values, fieldNames count isn't 2"));
|
||||
return results;
|
||||
}
|
||||
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
|
||||
|
||||
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
|
||||
Poco::Data::Statement select = _loadMultipleFromDB(session, fieldNames, conditionType);
|
||||
select, Poco::Data::Keywords::into(results),
|
||||
Poco::Data::Keywords::useRef(field1Value), Poco::Data::Keywords::useRef(field2Value);
|
||||
|
||||
size_t resultCount = 0;
|
||||
try {
|
||||
resultCount = select.execute();
|
||||
}
|
||||
catch (Poco::Exception& ex) {
|
||||
lock();
|
||||
addError(new ParamError(getTableName(), "mysql error by selecting with 2 different field types", ex.displayText()));
|
||||
int count = 0;
|
||||
for (auto it = fieldNames.begin(); it != fieldNames.end(); it++) {
|
||||
addError(new ParamError(getTableName(), "field name for select: ", *it));
|
||||
}
|
||||
|
||||
//addError(new ParamError(getTableName(), "field name for select: ", fieldName.data()));
|
||||
unlock();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
template<class Tuple, class T1, class T2, class T3, class T4>
|
||||
std::vector<Tuple> ModelBase::loadMultipleFromDB(
|
||||
const std::vector<std::string>& fieldNames,
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "JSONInterface/JsonCheckUsername.h"
|
||||
|
||||
TEST(TestJsonCheckUsername, InvalidGroupAlias)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("group_alias", "robert");
|
||||
auto result = jsonCall.handle(params);
|
||||
auto msg = result->get("msg");
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
ASSERT_TRUE(msg.isString());
|
||||
ASSERT_EQ(msg.toString(), "unknown group");
|
||||
|
||||
delete result;
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, InvalidGroupId)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("group_id", "4");
|
||||
auto result = jsonCall.handle(params);
|
||||
auto msg = result->get("msg");
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
ASSERT_TRUE(msg.isString());
|
||||
ASSERT_EQ(msg.toString(), "unknown group");
|
||||
|
||||
delete result;
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, ValidGroupAlias)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("group_alias", "gdd1");
|
||||
auto result = jsonCall.handle(params);
|
||||
auto state = result->get("state");
|
||||
ASSERT_FALSE(state.isEmpty());
|
||||
ASSERT_TRUE(state.isString());
|
||||
ASSERT_EQ(state.toString(), "success");
|
||||
|
||||
auto group_id = result->get("group_id");
|
||||
ASSERT_FALSE(group_id.isEmpty());
|
||||
ASSERT_TRUE(group_id.isInteger());
|
||||
int group_id_int = 0;
|
||||
group_id.convert(group_id_int);
|
||||
ASSERT_EQ(group_id_int, 1);
|
||||
|
||||
delete result;
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, UsernameWithoutGroup)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("username", "maxi");
|
||||
auto result = jsonCall.handle(params);
|
||||
|
||||
auto state = result->get("state");
|
||||
ASSERT_FALSE(state.isEmpty());
|
||||
ASSERT_TRUE(state.isString());
|
||||
ASSERT_EQ(state.toString(), "error");
|
||||
|
||||
auto msg = result->get("msg");
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
ASSERT_TRUE(msg.isString());
|
||||
ASSERT_EQ(msg.toString(), "no group given");
|
||||
|
||||
|
||||
delete result;
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, ExistingUsername)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("username", "Erfinder");
|
||||
params->set("group_id", 1);
|
||||
auto result = jsonCall.handle(params);
|
||||
|
||||
auto state = result->get("state");
|
||||
ASSERT_FALSE(state.isEmpty());
|
||||
ASSERT_TRUE(state.isString());
|
||||
ASSERT_EQ(state.toString(), "warning");
|
||||
|
||||
auto msg = result->get("msg");
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
ASSERT_TRUE(msg.isString());
|
||||
ASSERT_EQ(msg.toString(), "username already in use");
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, NewUsername)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("username", "Maxi");
|
||||
params->set("group_id", 1);
|
||||
auto result = jsonCall.handle(params);
|
||||
|
||||
auto state = result->get("state");
|
||||
ASSERT_FALSE(state.isEmpty());
|
||||
ASSERT_TRUE(state.isString());
|
||||
ASSERT_EQ(state.toString(), "success");
|
||||
}
|
||||
|
||||
TEST(TestJsonCheckUsername, UsernameExistInOtherGroup)
|
||||
{
|
||||
JsonCheckUsername jsonCall;
|
||||
Poco::JSON::Object::Ptr params = new Poco::JSON::Object;
|
||||
params->set("username", "Erfinder");
|
||||
params->set("group_id", 2);
|
||||
auto result = jsonCall.handle(params);
|
||||
|
||||
auto state = result->get("state");
|
||||
ASSERT_FALSE(state.isEmpty());
|
||||
ASSERT_TRUE(state.isString());
|
||||
ASSERT_EQ(state.toString(), "success");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -152,46 +152,38 @@ int load(int argc, char* argv[]) {
|
||||
Profiler timeUsed;
|
||||
|
||||
// clean up and fill db
|
||||
std::string tables[] = {
|
||||
"hedera_accounts",
|
||||
"hedera_ids",
|
||||
"crypto_keys",
|
||||
"hedera_topics",
|
||||
std::string tables[] = {
|
||||
"groups",
|
||||
"node_servers",
|
||||
"users"
|
||||
};
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
runMysql("TRUNCATE " + tables[i]);
|
||||
runMysql("ALTER TABLE " + tables[i] + " AUTO_INCREMENT = 1");
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "INSERT INTO `hedera_ids` (`id`, `shardNum`, `realmNum`, `num`) VALUES "
|
||||
<< "(1, 0, 0, 37281), "
|
||||
<< "(2, 0, 0, 21212), "
|
||||
<< "(3, 0, 0, 212);";
|
||||
ss << "INSERT INTO `users` (`id`, `email`, `first_name`, `last_name`, `username`, `password`, `pubkey`, `privkey`, `created`, `email_checked`, `passphrase_shown`, `language`, `disabled`, `group_id`) VALUES "
|
||||
<< "(1, 'd_schultz32@gmx.de', 'DDD', 'Schultz', 'Diddel', 13134558453895551556, 0x146d3fb9e88abc0fca0b0091c1ab1b32b399be037436f340befa8bf004461889, 0x0dcc08960f45f631fe23bc7ddee0724cedc9ec0c861ce30f5091d20ffd96062d08ca215726fb9bd64860c754772e945eea4cc872ed0a36c7b640e8b0bf7a873ec6765fa510711622341347ce2307b5ce, '2020-02-20 16:05:44', 1, 0, 'de', 0, 1), "
|
||||
<< "(2, 'Jeet_bb@gmail.com', 'Darios', 'Bruder', 'Jeet', 12910944485867375321, 0x952e215a21d4376b4ac252c4bf41e156e1498e1b6b8ccf2a6826d96712f4f461, 0x4d40bf0860655f728312140dc3741e897bc2d13d00ea80a63e2961046a5a7bd8315397dfb488b89377087bc1a5f4f3af8ffdcf203329ae23ba04be7d38ad3852699d90ff1fc00e5b1ca92b64cc59c01f, '2020-02-20 16:05:44', 1, 0, 'de', 0, 1), "
|
||||
<< "(3, 'Tiger_231@yahoo.com', 'Dieter', 'Schultz', 'Tiger', 13528673707291575501, 0xb539944bf6444a2bfc988244f0c0c9dc326452be9b8a2a43fcd90663719f4f6d, 0x5461fda60b719b65ba00bd6298e48410c4cbf0e89deb13cc784ba8978cf047454e8556ee3eddc8487ee835c33a83163bc8d8babbf2a5c431876bc0a0c114ff0a0d6b57baa12cf8f23c64fb642c862db5, '2020-02-20 16:05:45', 1, 0, 'de', 0, 1), "
|
||||
<< "(4, 'Nikola_Tesla@email.de', 'Nikola', 'Tesla', 'Erfinder', 15522411320147607375, 0x476b059744f08b0995522b484c90f8d2f47d9b59f4b3c96d9dc0ae6ab7b84979, 0x5277bf044cba4fec64e6f4d38da132755b029161231daefc9a7b4692ad37e05cdd88e0a2c2215baf854dd3a813578c214167af1113607e9f999ca848a7598ba5068e38f2a1afb097e4752a88024d79c8, '2020-02-20 16:05:46', 1, 0, 'de', 0, 1), "
|
||||
<< "(5, 'Elfenhausen@arcor.de', 'Thomas', 'Markuk', 'Elf', 7022671043835614958, 0xb1584e169d60a7e771d3a348235dfd7b5f9e8235fcc26090761a0264b0daa6ff, 0xb46fb7110bf91e28f367aa80f84d1bbd639b6f689f4b0aa28c0f71529232df9bf9ee0fb02fa4c1b9f5a6799c82d119e5646f7231d011517379faaacf6513d973ac3043d4c786490ba62d56d75b86164d, '2020-02-20 16:05:46', 1, 0, 'de', 0, 1), "
|
||||
<< "(6, 'coin-info12@gradido.net', 'coin-info12', 'Test', 'Test Username', 1548398919826089202, 0x4046ae49c1b620f2a321aba0c874fa2bc7ba844ab808bb0eeb18a908d468db14, 0x9522657ecd7456eedf86d065aa087ba7a94a8961a8e4950d044136155d38fe0840f2c0a2876ce055b3eaa6e9ab95c5feba89e535e0434fb2648d94d6e6ec68211aa2ea9e42d1ccd40b6b3c31e41f848e, '2020-02-20 16:05:47', 1, 0, 'de', 0, 1), "
|
||||
<< "(7, 'AlexWesper@gmail.com', 'Alex', 'Wesper', 'Wespe', 5822761891727948301, 0xb13ede3402abb8f29722b14fec0a2006ae7a3a51fb677cd6a2bbd797ac6905a5, 0x6aa39d7670c64a31639c7d89b874ad929b2eaeb2e5992dbad71b6cea700bf9e3c6cf866d0f0fdc22b44a0ebf51a860799e880ef86266199931dd0a301e5552db44b9b7fa99ed5945652bc7b31eff767c, '2020-02-20 16:05:47', 1, 0, 'de', 0, 1); ";
|
||||
runMysql(ss.str());
|
||||
ss.str(std::string());
|
||||
|
||||
ss << "INSERT INTO `crypto_keys` (`id`, `private_key`, `public_key`, `crypto_key_type_id`) VALUES "
|
||||
<< "(1, 0x263f1da712c3b47286b463c2de3784f364f2534d2c34722a3b483c3f3e36476857564f564d476c32d3e342f5ef2763cd23e23a2b429bab62e352f46ba273e2f2, 0xfe5237c2d1ab1361b33163f15634e261c1d217ae32b327cbd88db8ebffedb271, 3), "
|
||||
<< "(2, 0x721f3e73e3263f1da712c3b47286b463c2de3784f364f2534d2c34722a3b483c3f3e36476857564f564d476c32d3e342f5ef2763cd23e23a2b429bab62e352f46ba273e2f2ef3264fe2452da62bc2739, 0xe3f253d1a2deb25362d2e374baf37bc1d3ef3781cfe1e127f3cd0abcdf372ea6, 1); ";
|
||||
ss << "INSERT INTO `groups` (`id`, `alias`, `name`, `url`, `description`) VALUES"
|
||||
<< "(1, 'gdd1', 'Gradido1', 'gdd1.gradido.com', 'Der erste offizielle Gradido Server (zum Testen)'), "
|
||||
<< "(2, 'gdd_test', 'Gradido Test', 'gdd1.gradido.com', 'Testgroup (zum Testen)'); ";
|
||||
runMysql(ss.str());
|
||||
ss.str(std::string());
|
||||
|
||||
ss << "INSERT INTO `hedera_accounts` (`id`, `user_id`, `account_hedera_id`, `account_key_id`, `balance`, `network_type`, `updated`) VALUES "
|
||||
<< "(1, 1, 1, 1, 1000000000000, 1, '2019-09-03 11:13:52'), "
|
||||
<< "(2, 1, 2, 2, 4312881211, 0, '2019-09-03 11:13:56'); ";
|
||||
runMysql(ss.str());
|
||||
ss.str(std::string());
|
||||
|
||||
ss << "INSERT INTO `hedera_topics` (`id`, `topic_hedera_id`, `name`, `auto_renew_account_hedera_id`, `auto_renew_period`, `group_id`, `admin_key_id`, `submit_key_id`, `current_timeout`, `sequence_number`, `updated`) VALUES "
|
||||
<< "(1, 3, 'gdd_test_topic', 1, 0, 1, NULL, NULL, '1999-12-31 23:00:00', 0, '2020-09-14 18:29:04'); ";
|
||||
runMysql(ss.str());
|
||||
ss.str(std::string());
|
||||
std::clog << "after inserting everything in db" << std::endl;
|
||||
|
||||
|
||||
printf("init db in : %s\n", timeUsed.string().data());
|
||||
|
||||
|
||||
fillTests();
|
||||
for (std::list<Test*>::iterator it = gTests.begin(); it != gTests.end(); it++)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user