Merge branch 'master' into last-community_server

This commit is contained in:
Hannes Heine 2022-02-03 04:50:13 +01:00 committed by GitHub
commit 19f6e1fc63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1316 additions and 1124 deletions

View File

@ -448,7 +448,7 @@ jobs:
report_name: Coverage Admin Interface
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 78
min_coverage: 81
token: ${{ github.token }}
##############################################################################

View File

@ -12,7 +12,7 @@
"dev": "yarn run serve",
"build": "vue-cli-service build",
"lint": "eslint --ext .js,.vue .",
"test": "jest --coverage",
"test": "TZ=UTC jest --coverage",
"locales": "scripts/missing-keys.sh && scripts/sort.sh"
},
"dependencies": {
@ -36,14 +36,12 @@
"graphql": "^15.6.1",
"identity-obj-proxy": "^3.0.0",
"jest": "26.6.3",
"moment": "^2.29.1",
"regenerator-runtime": "^0.13.9",
"stats-webpack-plugin": "^0.7.0",
"vue": "^2.6.11",
"vue-apollo": "^3.0.8",
"vue-i18n": "^8.26.5",
"vue-jest": "^3.0.7",
"vue-moment": "^4.1.0",
"vue-router": "^3.5.3",
"vue-toasted": "^1.1.28",
"vuex": "^3.6.2",

View File

@ -5,14 +5,6 @@ import { createPendingCreations } from '../graphql/createPendingCreations'
const localVue = global.localVue
const apolloMock = jest.fn().mockResolvedValue({
data: {
verifyLogin: {
name: 'success',
id: 0,
},
},
})
const apolloMutateMock = jest.fn().mockResolvedValue({
data: {
createPendingCreation: [0, 0, 0],
@ -23,19 +15,12 @@ const toastedErrorMock = jest.fn()
const toastedSuccessMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$moment: jest.fn(() => {
return {
format: jest.fn((m) => m),
subtract: jest.fn(() => {
return {
format: jest.fn((m) => m),
}
}),
}
$t: jest.fn((t, options) => (options ? [t, options] : t)),
$d: jest.fn((d) => {
const date = new Date(d)
return date.toISOString().split('T')[0]
}),
$apollo: {
query: apolloMock,
mutate: apolloMutateMock,
},
$store: {
@ -56,7 +41,12 @@ const mocks = {
const propsData = {
type: '',
creation: [],
itemsMassCreation: {},
}
const now = new Date(Date.now())
const getCreationDate = (sub) => {
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
return date.toISOString().split('T')[0]
}
describe('CreationFormular', () => {
@ -75,21 +65,24 @@ describe('CreationFormular', () => {
expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy()
})
describe('server sends back moderator data', () => {
it('called store commit with mocked data', () => {
expect(stateCommitMock).toBeCalledWith('moderator', { name: 'success', id: 0 })
})
})
describe('server throws error for moderator data call', () => {
beforeEach(() => {
jest.clearAllMocks()
apolloMock.mockRejectedValueOnce({ message: 'Ouch!' })
wrapper = Wrapper()
describe('text and value form props', () => {
beforeEach(async () => {
wrapper = mount(CreationFormular, {
localVue,
mocks,
propsData: {
creationUserData: { memo: 'Memo from property', amount: 42 },
...propsData,
},
})
})
it('has called store commit with fake data', () => {
expect(stateCommitMock).toBeCalledWith('moderator', { id: 0, name: 'Test Moderator' })
it('has text taken from props', () => {
expect(wrapper.vm.text).toBe('Memo from property')
})
it('has value taken from props', () => {
expect(wrapper.vm.value).toBe(42)
})
})
@ -98,52 +91,6 @@ describe('CreationFormular', () => {
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
})
describe('with mass creation', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper.setProps({ type: 'massCreation', creation: [200, 400, 600] })
await wrapper.setData({ rangeMin: 180 })
await wrapper.setData({ text: 'Test create coins' })
await wrapper.setData({ value: 90 })
})
describe('first radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
})
it('emits update-radio-selected with index 0', () => {
expect(wrapper.emitted()['update-radio-selected']).toEqual([
[expect.arrayContaining([0])],
])
})
})
describe('second radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
})
it('emits update-radio-selected with index 1', () => {
expect(wrapper.emitted()['update-radio-selected']).toEqual([
[expect.arrayContaining([1])],
])
})
})
describe('third radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
})
it('emits update-radio-selected with index 2', () => {
expect(wrapper.emitted()['update-radio-selected']).toEqual([
[expect.arrayContaining([2])],
])
})
})
})
describe('with single creation', () => {
beforeEach(async () => {
jest.clearAllMocks()
@ -153,17 +100,17 @@ describe('CreationFormular', () => {
item: { email: 'benjamin@bluemchen.de' },
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test create coins')
await wrapper.find('input[type="number"]').setValue(90)
})
describe('first radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
await wrapper.find('textarea').setValue('Test create coins')
})
it('sets rangeMax to 200', () => {
expect(wrapper.vm.rangeMax).toBe(400)
expect(wrapper.vm.rangeMax).toBe(200)
})
describe('sendForm', () => {
@ -177,7 +124,7 @@ describe('CreationFormular', () => {
mutation: createPendingCreation,
variables: {
email: 'benjamin@bluemchen.de',
creationDate: 'YYYY-MM-01',
creationDate: getCreationDate(2),
amount: 90,
memo: 'Test create coins',
moderator: 0,
@ -185,9 +132,30 @@ describe('CreationFormular', () => {
}),
)
})
it('emits update-user-data', () => {
expect(wrapper.emitted('update-user-data')).toEqual([
[{ email: 'benjamin@bluemchen.de' }, [0, 0, 0]],
])
})
it('toasts a success message', () => {
expect(toastedSuccessMock).toBeCalledWith([
'creation_form.toasted',
{ email: 'benjamin@bluemchen.de', value: '90' },
])
})
it('updates open creations in store', () => {
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 1)
})
it('resets the form data', () => {
expect(wrapper.vm.value).toBe(0)
})
})
describe('sendForm', () => {
describe('sendForm with server error', () => {
beforeEach(async () => {
apolloMutateMock.mockRejectedValueOnce({ message: 'Ouch!' })
await wrapper.find('.test-submit').trigger('click')
@ -373,9 +341,18 @@ describe('CreationFormular', () => {
})
})
describe('with mass creation', () => {
describe('mass creation with success', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockResolvedValue({
data: {
createPendingCreations: {
success: true,
successfulCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'],
failedCreation: [],
},
},
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
@ -395,14 +372,14 @@ describe('CreationFormular', () => {
pendingCreations: [
{
email: 'bob@baumeister.de',
creationDate: 'YYYY-MM-01',
creationDate: getCreationDate(1),
amount: 200,
memo: 'Test mass create coins',
moderator: 0,
},
{
email: 'bibi@bloxberg.de',
creationDate: 'YYYY-MM-01',
creationDate: getCreationDate(1),
amount: 200,
memo: 'Test mass create coins',
moderator: 0,
@ -412,6 +389,73 @@ describe('CreationFormular', () => {
}),
)
})
it('updates open creations in store', () => {
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 2)
})
it('emits remove-all-bookmark', () => {
expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy()
})
})
describe('mass creation with success but all failed', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockResolvedValue({
data: {
createPendingCreations: {
success: true,
successfulCreation: [],
failedCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'],
},
},
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test mass create coins')
await wrapper.find('input[type="number"]').setValue(200)
await wrapper.find('.test-submit').trigger('click')
})
it('updates open creations in store', () => {
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 0)
})
it('toasts two errors', () => {
expect(toastedErrorMock).toBeCalledWith(
'Could not created PendingCreation for bob@baumeister.de',
)
expect(toastedErrorMock).toBeCalledWith(
'Could not created PendingCreation for bibi@bloxberg.de',
)
})
})
describe('mass creation with error', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockRejectedValue({
message: 'Oh no!',
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test mass create coins')
await wrapper.find('input[type="number"]').setValue(200)
await wrapper.find('.test-submit').trigger('click')
})
it('toasts an error message', () => {
expect(toastedErrorMock).toBeCalledWith('Oh no!')
})
})
})
})

View File

@ -7,51 +7,15 @@
<label>{{ $t('creation_form.select_month') }}</label>
</div>
<b-row class="ml-4">
<b-col>
<b-form-radio
id="beforeLastMonth"
v-model="radioSelected"
:value="beforeLastMonth"
:disabled="creation[0] === 0"
size="lg"
@change="updateRadioSelected(beforeLastMonth, 0, creation[0])"
>
<label for="beforeLastMonth">
{{ beforeLastMonth.short }} {{ creation[0] != null ? creation[0] + ' GDD' : '' }}
</label>
</b-form-radio>
</b-col>
<b-col>
<b-form-radio
id="lastMonth"
v-model="radioSelected"
:value="lastMonth"
:disabled="creation[1] === 0"
size="lg"
@change="updateRadioSelected(lastMonth, 1, creation[1])"
>
<label for="lastMonth">
{{ lastMonth.short }} {{ creation[1] != null ? creation[1] + ' GDD' : '' }}
</label>
</b-form-radio>
</b-col>
<b-col>
<b-form-radio
id="currentMonth"
v-model="radioSelected"
:value="currentMonth"
:disabled="creation[2] === 0"
size="lg"
@change="updateRadioSelected(currentMonth, 2, creation[2])"
>
<label for="currentMonth">
{{ currentMonth.short }} {{ creation[2] != null ? creation[2] + ' GDD' : '' }}
</label>
</b-form-radio>
</b-col>
<b-form-radio-group
v-model="selected"
:options="radioOptions"
value-field="item"
text-field="name"
name="month-selection"
></b-form-radio-group>
</b-row>
<div class="m-4" v-show="createdIndex != null">
<b-row class="m-4" v-show="selected !== ''">
<label>{{ $t('creation_form.select_value') }}</label>
<div>
<b-input-group prepend="GDD" append=".00">
@ -62,7 +26,6 @@
:max="rangeMax"
></b-form-input>
</b-input-group>
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
<b-form-input
type="range"
@ -73,7 +36,7 @@
></b-form-input>
</b-input-group>
</div>
</div>
</b-row>
<div class="m-4">
<label>{{ $t('creation_form.enter_text') }}</label>
<div>
@ -100,18 +63,17 @@
variant="success"
class="test-submit"
@click="submitCreation"
:disabled="radioSelected === '' || value <= 0 || text.length < 10"
:disabled="selected === '' || value <= 0 || text.length < 10"
>
{{ $t('creation_form.update_creation') }}
</b-button>
<b-button
v-else
type="button"
variant="success"
class="test-submit"
@click="submitCreation"
:disabled="radioSelected === '' || value <= 0 || text.length < 10"
:disabled="selected === '' || value <= 0 || text.length < 10"
>
{{ $t('creation_form.submit_creation') }}
</b-button>
@ -123,11 +85,12 @@
</div>
</template>
<script>
import { verifyLogin } from '../graphql/verifyLogin'
import { createPendingCreation } from '../graphql/createPendingCreation'
import { createPendingCreations } from '../graphql/createPendingCreations'
import { creationMonths } from '../mixins/creationMonths'
export default {
name: 'CreationFormular',
mixins: [creationMonths],
props: {
type: {
type: String,
@ -166,71 +129,39 @@ export default {
},
data() {
return {
radioSelected: '',
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
rangeMin: 0,
rangeMax: 1000,
currentMonth: {
short: this.$moment().format('MMMM'),
long: this.$moment().format('YYYY-MM-DD'),
year: this.$moment().format('YYYY'),
},
lastMonth: {
short: this.$moment().subtract(1, 'month').format('MMMM'),
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
year: this.$moment().subtract(1, 'month').format('YYYY'),
},
beforeLastMonth: {
short: this.$moment().subtract(2, 'month').format('MMMM'),
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
year: this.$moment().subtract(2, 'month').format('YYYY'),
},
submitObj: null,
isdisabled: true,
createdIndex: null,
selected: '',
}
},
methods: {
// Auswählen eines Zeitraumes
updateRadioSelected(name, index, openCreation) {
this.createdIndex = index
updateRadioSelected(name) {
// do we want to reset the memo everytime the month changes?
this.text = this.$t('creation_form.creation_for') + ' ' + name.short + ' ' + name.year
// Wenn Mehrfachschöpfung
if (this.type === 'massCreation') {
// An Creation.vue emitten und radioSelectedMass aktualisieren
this.$emit('update-radio-selected', [name, index])
} else if (this.type === 'singleCreation') {
if (this.type === 'singleCreation') {
this.rangeMin = 0
// Der maximale offene Betrag an GDD die für ein User noch geschöpft werden kann
this.rangeMax = openCreation
this.rangeMax = name.creation
}
},
submitCreation() {
let submitObj = []
if (this.type === 'massCreation') {
// Die anzahl der Mitglieder aus der Mehrfachschöpfung
const i = Object.keys(this.items).length
// hinweis das eine Mehrfachschöpfung ausgeführt wird an (Anzahl der MItgleider an die geschöpft wird)
// eslint-disable-next-line no-console
console.log('SUBMIT CREATION => ' + this.type + ' >> für VIELE ' + i + ' Mitglieder')
this.submitObj = []
this.items.forEach((item) => {
this.submitObj.push({
submitObj.push({
email: item.email,
creationDate: this.radioSelected.long,
creationDate: this.selected.date,
amount: Number(this.value),
memo: this.text,
moderator: Number(this.$store.state.moderator.id),
})
})
// eslint-disable-next-line no-console
console.log('MehrfachSCHÖPFUNG ABSENDEN FÜR >> ' + i + ' Mitglieder')
this.$apollo
.mutate({
mutation: createPendingCreations,
variables: {
pendingCreations: this.submitObj,
pendingCreations: submitObj,
},
fetchPolicy: 'no-cache',
})
@ -241,6 +172,7 @@ export default {
)
if (result.data.createPendingCreations.failedCreation.length > 0) {
result.data.createPendingCreations.failedCreation.forEach((failed) => {
// TODO: Please localize this error message
this.$toasted.error('Could not created PendingCreation for ' + failed)
})
}
@ -250,18 +182,17 @@ export default {
this.$toasted.error(error.message)
})
} else if (this.type === 'singleCreation') {
this.submitObj = {
submitObj = {
email: this.item.email,
creationDate: this.radioSelected.long,
creationDate: this.selected.date,
amount: Number(this.value),
memo: this.text,
moderator: Number(this.$store.state.moderator.id),
}
this.$apollo
.mutate({
mutation: createPendingCreation,
variables: this.submitObj,
variables: submitObj,
})
.then((result) => {
this.$emit('update-user-data', this.item, result.data.createPendingCreation)
@ -272,36 +203,22 @@ export default {
}),
)
this.$store.commit('openCreationsPlus', 1)
this.submitObj = null
this.createdIndex = null
// das creation Formular reseten
// what is this? Tests says that this.text is not reseted
this.$refs.creationForm.reset()
// Den geschöpften Wert auf o setzen
this.value = 0
})
.catch((error) => {
this.$toasted.error(error.message)
this.submitObj = null
// das creation Formular reseten
this.$refs.creationForm.reset()
// Den geschöpften Wert auf o setzen
this.value = 0
})
}
},
searchModeratorData() {
this.$apollo
.query({ query: verifyLogin })
.then((result) => {
this.$store.commit('moderator', result.data.verifyLogin)
})
.catch(() => {
this.$store.commit('moderator', { id: 0, name: 'Test Moderator' })
})
},
},
created() {
this.searchModeratorData()
watch: {
selected() {
this.updateRadioSelected(this.selected)
},
},
}
</script>

View File

@ -7,8 +7,9 @@ const apolloMutateMock = jest.fn().mockResolvedValue({
data: {
updatePendingCreation: {
creation: [0, 0, 0],
amount: 500,
date: new Date(),
memo: 'qwertzuiopasdfghjkl',
memo: 'Test Schöpfung 2',
moderator: 0,
},
},
@ -20,15 +21,9 @@ const toastedSuccessMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$moment: jest.fn(() => {
return {
format: jest.fn((m) => m),
subtract: jest.fn(() => {
return {
format: jest.fn((m) => m),
}
}),
}
$d: jest.fn((d) => {
const date = new Date(d)
return date.toISOString().split('T')[0]
}),
$apollo: {
mutate: apolloMutateMock,
@ -48,12 +43,18 @@ const mocks = {
},
}
const now = new Date(Date.now())
const getCreationDate = (sub) => {
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
return date.toISOString().split('T')[0]
}
const propsData = {
creation: [200, 400, 600],
creationUserData: {
memo: 'Test schöpfung 1',
amount: 100,
date: '2021-12-01',
date: getCreationDate(0),
},
item: {
id: 0,
@ -82,196 +83,79 @@ describe('EditCreationFormular', () => {
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
})
describe('with single creation', () => {
it('has the third radio button checked', () => {
expect(wrapper.findAll('input[type="radio"]').at(0).element.checked).toBeFalsy()
expect(wrapper.findAll('input[type="radio"]').at(1).element.checked).toBeFalsy()
expect(wrapper.findAll('input[type="radio"]').at(2).element.checked).toBeTruthy()
})
it('has rangeMax of 700', () => {
expect(wrapper.find('input[type="number"]').attributes('max')).toBe('700')
})
describe('change and save memo and value with success', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper.setProps({ creation: [200, 400, 600] })
await wrapper.setData({ rangeMin: 180 })
await wrapper.setData({ text: 'Test create coins' })
await wrapper.setData({ value: 90 })
await wrapper.find('input[type="number"]').setValue(500)
await wrapper.find('textarea').setValue('Test Schöpfung 2')
await wrapper.find('.test-submit').trigger('click')
})
describe('first radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
})
it('sets rangeMin to 0', () => {
expect(wrapper.vm.rangeMin).toBe(0)
})
it('sets rangeMax to 200', () => {
expect(wrapper.vm.rangeMax).toBe(200)
})
describe('sendForm', () => {
beforeEach(async () => {
await wrapper.find('.test-submit').trigger('click')
})
it('sends ... to apollo', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
variables: {
amount: 90,
creationDate: 'YYYY-MM-01',
email: 'bob@baumeister.de',
id: 0,
memo: 'Test create coins',
moderator: 0,
},
}),
)
})
it('emits update-user-data', () => {
expect(wrapper.emitted('update-user-data')).toBeTruthy()
expect(wrapper.emitted('update-user-data')).toEqual([
[
{
id: 0,
email: 'bob@baumeister.de',
},
[0, 0, 0],
],
])
})
it('toast success message', () => {
expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_update')
})
describe('sendForm with error', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockRejectedValue({
message: 'Ouch!',
})
wrapper = Wrapper()
await wrapper.setProps({ type: 'singleCreation', creation: [200, 400, 600] })
await wrapper.setData({ text: 'Test create coins' })
await wrapper.setData({ value: 90 })
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
await wrapper.setData({ rangeMin: 100 })
await wrapper.find('.test-submit').trigger('click')
})
it('toast error message', () => {
expect(toastedErrorMock).toBeCalledWith('Ouch!')
})
})
})
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
variables: {
id: 0,
email: 'bob@baumeister.de',
creationDate: getCreationDate(0),
amount: 500,
memo: 'Test Schöpfung 2',
moderator: 0,
},
}),
)
})
describe('second radio button', () => {
beforeEach(async () => {
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
})
it('sets rangeMin to 0', () => {
expect(wrapper.vm.rangeMin).toBe(0)
})
it('sets rangeMax to 400', () => {
expect(wrapper.vm.rangeMax).toBe(400)
})
describe('sendForm', () => {
beforeEach(async () => {
await wrapper.find('.test-submit').trigger('click')
})
it('sends ... to apollo', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
variables: {
amount: 90,
creationDate: 'YYYY-MM-01',
email: 'bob@baumeister.de',
id: 0,
memo: 'Test create coins',
moderator: 0,
},
}),
)
})
describe('sendForm with error', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockRejectedValue({
message: 'Ouch!',
})
wrapper = Wrapper()
await wrapper.setProps({ creation: [200, 400, 600] })
await wrapper.setData({ text: 'Test create coins' })
await wrapper.setData({ value: 100 })
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.setData({ rangeMin: 180 })
await wrapper.find('.test-submit').trigger('click')
})
it('toast error message', () => {
expect(toastedErrorMock).toBeCalledWith('Ouch!')
})
})
})
it('emits update-user-data', () => {
expect(wrapper.emitted('update-user-data')).toEqual([
[
{
id: 0,
email: 'bob@baumeister.de',
},
[0, 0, 0],
],
])
})
describe('third radio button', () => {
beforeEach(async () => {
await wrapper.setData({ rangeMin: 180 })
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
})
it('emits update-creation-data', () => {
expect(wrapper.emitted('update-creation-data')).toEqual([
[
{
amount: 500,
date: expect.any(Date),
memo: 'Test Schöpfung 2',
moderator: 0,
row: expect.any(Object),
},
],
])
})
it('sets rangeMin to 180', () => {
expect(wrapper.vm.rangeMin).toBe(180)
})
it('toasts a success message', () => {
expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_update')
})
})
it('sets rangeMax to 700', () => {
expect(wrapper.vm.rangeMax).toBe(700)
})
describe('change and save memo and value with error', () => {
beforeEach(async () => {
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('input[type="number"]').setValue(500)
await wrapper.find('textarea').setValue('Test Schöpfung 2')
await wrapper.find('.test-submit').trigger('click')
})
describe('sendForm with success', () => {
beforeEach(async () => {
await wrapper.find('.test-submit').trigger('click')
})
it('sends ... to apollo', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
variables: {
amount: 90,
creationDate: 'YYYY-MM-DD',
email: 'bob@baumeister.de',
id: 0,
memo: 'Test create coins',
moderator: 0,
},
}),
)
})
})
describe('sendForm with error', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockRejectedValue({
message: 'Ouch!',
})
wrapper = Wrapper()
await wrapper.setProps({ creation: [200, 400, 600] })
await wrapper.setData({ text: 'Test create coins' })
await wrapper.setData({ value: 90 })
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
await wrapper.setData({ rangeMin: 180 })
await wrapper.find('.test-submit').trigger('click')
})
it('toast error message', () => {
expect(toastedErrorMock).toBeCalledWith('Ouch!')
})
})
it('toasts an error message', () => {
expect(toastedErrorMock).toBeCalledWith('Oh no!')
})
})
})

View File

@ -6,65 +6,14 @@
<label>{{ $t('creation_form.select_month') }}</label>
</div>
<b-row class="m-4">
<b-col class="text-left">
<b-form-radio
id="beforeLastMonth"
v-model="radioSelected"
:value="beforeLastMonth"
:disabled="selectedOpenCreationAmount[0] === 0"
size="lg"
@change="updateRadioSelected(beforeLastMonth, 0, selectedOpenCreationAmount[0])"
>
<label for="beforeLastMonth">
{{ beforeLastMonth.short }}
{{
selectedOpenCreationAmount[0] != null
? selectedOpenCreationAmount[0] + ' GDD'
: ''
}}
</label>
</b-form-radio>
</b-col>
<b-col>
<b-form-radio
id="lastMonth"
v-model="radioSelected"
:value="lastMonth"
:disabled="selectedOpenCreationAmount[1] === 0"
size="lg"
@change="updateRadioSelected(lastMonth, 1, selectedOpenCreationAmount[1])"
>
<label for="lastMonth">
{{ lastMonth.short }}
{{
selectedOpenCreationAmount[1] != null
? selectedOpenCreationAmount[1] + ' GDD'
: ''
}}
</label>
</b-form-radio>
</b-col>
<b-col class="text-right">
<b-form-radio
id="currentMonth"
v-model="radioSelected"
:value="currentMonth"
:disabled="selectedOpenCreationAmount[2] === 0"
size="lg"
@change="updateRadioSelected(currentMonth, 2, selectedOpenCreationAmount[2])"
>
<label for="currentMonth">
{{ currentMonth.short }}
{{
selectedOpenCreationAmount[2] != null
? selectedOpenCreationAmount[2] + ' GDD'
: ''
}}
</label>
</b-form-radio>
</b-col>
<b-form-radio-group
v-model="selected"
:options="radioOptions"
value-field="item"
text-field="name"
name="month-selection"
></b-form-radio-group>
</b-row>
<div class="m-4">
<label>{{ $t('creation_form.select_value') }}</label>
<div>
@ -76,7 +25,6 @@
:max="rangeMax"
></b-form-input>
</b-input-group>
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
<b-form-input
type="range"
@ -113,7 +61,7 @@
variant="success"
class="test-submit"
@click="submitCreation"
:disabled="radioSelected === '' || value <= 0 || text.length < 10"
:disabled="selected === '' || value <= 0 || text.length < 10"
>
{{ $t('creation_form.update_creation') }}
</b-button>
@ -126,8 +74,11 @@
</template>
<script>
import { updatePendingCreation } from '../graphql/updatePendingCreation'
import { creationMonths } from '../mixins/creationMonths'
export default {
name: 'EditCreationFormular',
mixins: [creationMonths],
props: {
item: {
type: Object,
@ -151,51 +102,26 @@ export default {
},
data() {
return {
radioSelected: '',
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
rangeMin: 0,
rangeMax: 1000,
currentMonth: {
short: this.$moment().format('MMMM'),
long: this.$moment().format('YYYY-MM-DD'),
},
lastMonth: {
short: this.$moment().subtract(1, 'month').format('MMMM'),
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
},
beforeLastMonth: {
short: this.$moment().subtract(2, 'month').format('MMMM'),
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
},
submitObj: null,
isdisabled: true,
createdIndex: null,
selectedOpenCreationAmount: {},
selected: '',
}
},
methods: {
updateRadioSelected(name, index, openCreation) {
this.createdIndex = index
this.rangeMin = 0
this.rangeMax = this.creation[index]
},
submitCreation() {
this.submitObj = {
id: this.item.id,
email: this.item.email,
creationDate: this.radioSelected.long,
amount: Number(this.value),
memo: this.text,
moderator: Number(this.$store.state.moderator.id),
}
// hinweis das eine ein einzelne Schöpfung abgesendet wird an (email)
this.$apollo
.mutate({
mutation: updatePendingCreation,
variables: this.submitObj,
variables: {
id: this.item.id,
email: this.item.email,
creationDate: this.selected.date,
amount: Number(this.value),
memo: this.text,
moderator: Number(this.$store.state.moderator.id),
},
})
.then((result) => {
this.$emit('update-user-data', this.item, result.data.updatePendingCreation.creation)
@ -212,8 +138,6 @@ export default {
email: this.item.email,
}),
)
this.submitObj = null
this.createdIndex = null
// das creation Formular reseten
this.$refs.updateCreationForm.reset()
// Den geschöpften Wert auf o setzen
@ -221,7 +145,6 @@ export default {
})
.catch((error) => {
this.$toasted.error(error.message)
this.submitObj = null
// das creation Formular reseten
this.$refs.updateCreationForm.reset()
// Den geschöpften Wert auf o setzen
@ -231,25 +154,10 @@ export default {
},
created() {
if (this.creationUserData.date) {
switch (this.$moment(this.creationUserData.date).format('MMMM')) {
case this.currentMonth.short:
this.createdIndex = 2
this.radioSelected = this.currentMonth
break
case this.lastMonth.short:
this.createdIndex = 1
this.radioSelected = this.lastMonth
break
case this.beforeLastMonth.short:
this.createdIndex = 0
this.radioSelected = this.beforeLastMonth
break
default:
throw new Error('Something went wrong')
}
this.selectedOpenCreationAmount[this.createdIndex] =
this.creation[this.createdIndex] + this.creationUserData.amount
this.rangeMax = this.selectedOpenCreationAmount[this.createdIndex]
const month = this.$d(new Date(this.creationUserData.date), 'month')
const index = this.radioOptions.findIndex((obj) => obj.item.short === month)
this.selected = this.radioOptions[index].item
this.rangeMax = this.creation[index] + this.creationUserData.amount
}
},
}

View File

@ -107,16 +107,7 @@ describe('UserTable', () => {
const mocks = {
$t: jest.fn((t) => t),
$moment: jest.fn(() => {
return {
format: jest.fn((m) => m),
subtract: jest.fn(() => {
return {
format: jest.fn((m) => m),
}
}),
}
}),
$d: jest.fn((d) => d),
$apollo: {
query: apolloQueryMock,
},
@ -199,11 +190,6 @@ describe('UserTable', () => {
})
})
})
// it('expect(wrapper.html()).', () => {
// // eslint-disable-next-line no-console
// console.log(wrapper.html())
// })
})
describe('type UserListSearch', () => {
@ -214,11 +200,6 @@ describe('UserTable', () => {
it('has a DIV element with the class.component-user-table', () => {
expect(wrapper.find('.component-user-table').exists()).toBeTruthy()
})
// it('expect(wrapper.html()).', () => {
// // eslint-disable-next-line no-console
// console.log(wrapper.html())
// })
})
describe('type UserListMassCreation', () => {
@ -229,11 +210,6 @@ describe('UserTable', () => {
it('has a DIV element with the class.component-user-table', () => {
expect(wrapper.find('.component-user-table').exists()).toBeTruthy()
})
// it('expect(wrapper.html()).', () => {
// // eslint-disable-next-line no-console
// console.log(wrapper.html())
// })
})
describe('type PageCreationConfirm', () => {
@ -244,48 +220,6 @@ describe('UserTable', () => {
it('has a DIV element with the class.component-user-table', () => {
expect(wrapper.find('.component-user-table').exists()).toBeTruthy()
})
// it('expect(wrapper.html()).', () => {
// // eslint-disable-next-line no-console
// console.log(wrapper.html())
// })
})
/**
<user-table
v-if="itemsList.length > 0"
type="UserListSearch"
:itemsUser="itemsList"
:fieldsTable="Searchfields"
:criteria="criteria"
:creation="creation"
@update-item="updateItem"
/>
<user-table
v-show="itemsMassCreation.length > 0"
class="shadow p-3 mb-5 bg-white rounded"
type="UserListMassCreation"
:itemsUser="itemsMassCreation"
:fieldsTable="fields"
:criteria="null"
:creation="creation"
@update-item="updateItem"
/>
<user-table
class="mt-4"
type="PageCreationConfirm"
:itemsUser="confirmResult"
:fieldsTable="fields"
@remove-confirm-result="removeConfirmResult"
/>
<user-table
type="PageUserSearch"
:itemsUser="searchResult"
:fieldsTable="fields"
:criteria="criteria"
/>
*/
})
})

View File

@ -116,7 +116,7 @@
<confirm-register-mail-formular
:checked="row.item.emailChecked"
:email="row.item.email"
:dateLastSend="$moment().subtract(1, 'month').format('dddd, DD.MMMM.YYYY HH:mm'),"
:dateLastSend="$d(new Date(), 'long')"
/>
</template>
<template #show-transaction-list>
@ -287,11 +287,11 @@ export default {
this.overlay = false
},
bookmarkPush(item) {
this.$emit('update-item', item, 'push')
this.$emit('push-item', item)
},
bookmarkRemove(item) {
if (this.type === 'UserListMassCreation') {
this.$emit('update-item', item, 'remove')
this.$emit('remove-item', item)
}
if (this.type === 'PageCreationConfirm') {

View File

@ -60,6 +60,15 @@ const dateTimeFormats = {
hour: 'numeric',
minute: 'numeric',
},
monthShort: {
month: 'short',
},
month: {
month: 'long',
},
year: {
year: 'numeric',
},
},
de: {
short: {
@ -75,6 +84,15 @@ const dateTimeFormats = {
hour: 'numeric',
minute: 'numeric',
},
monthShort: {
month: 'short',
},
month: {
month: 'long',
},
year: {
year: 'numeric',
},
},
}

View File

@ -18,6 +18,7 @@
"toasted_update": "`Offene Schöpfung {value} GDD) für {email} wurde geändert und liegt zur Bestätigung bereit",
"update_creation": "Schöpfung aktualisieren"
},
"date": "Datum",
"details": "Details",
"e_mail": "E-Mail",
"firstname": "Vorname",

View File

@ -18,6 +18,7 @@
"toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.",
"update_creation": "Creation update"
},
"date": "Date",
"details": "Details",
"e_mail": "E-mail",
"firstname": "Firstname",

View File

@ -17,7 +17,6 @@ import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import moment from 'vue-moment'
import Toasted from 'vue-toasted'
import { apolloProvider } from './plugins/apolloProvider'
@ -26,8 +25,6 @@ Vue.use(BootstrapVue)
Vue.use(IconsPlugin)
Vue.use(moment)
Vue.use(VueApollo)
Vue.use(Toasted, {
@ -48,7 +45,6 @@ i18n.locale =
store.state.moderator && store.state.moderator.language ? store.state.moderator.language : 'en'
new Vue({
moment,
router,
store,
i18n,

View File

@ -6,7 +6,6 @@ import Vue from 'vue'
import VueApollo from 'vue-apollo'
import i18n from './i18n'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import moment from 'vue-moment'
import store from './store/store'
import router from './router/router'
@ -14,7 +13,6 @@ jest.mock('vue')
jest.mock('vue-apollo')
jest.mock('vuex')
jest.mock('vue-i18n')
jest.mock('vue-moment')
jest.mock('./store/store', () => {
return {
state: {
@ -90,10 +88,6 @@ describe('main', () => {
expect(Vue.use).toBeCalledWith(IconsPlugin)
})
it('calls Moment', () => {
expect(Vue.use).toBeCalledWith(moment)
})
it('creates a store', () => {
expect(Vue).toBeCalledWith(
expect.objectContaining({

View File

@ -0,0 +1,35 @@
export const creationMonths = {
props: {
creation: [1000, 1000, 1000],
},
computed: {
creationDates() {
const now = new Date(Date.now())
const dates = [now]
for (let i = 1; i < 3; i++) {
dates.push(new Date(now.getFullYear(), now.getMonth() - i, 1))
}
return dates.reverse()
},
creationDateObjects() {
const result = []
this.creationDates.forEach((date) => {
result.push({
short: this.$d(date, 'month'),
long: this.$d(date, 'short'),
year: this.$d(date, 'year'),
date: this.$d(date, 'short', 'en'),
})
})
return result
},
radioOptions() {
return this.creationDateObjects.map((obj, idx) => {
return {
item: { ...obj, creation: this.creation[idx] },
name: obj.short + (this.creation[idx] ? ' ' + this.creation[idx] + ' GDD' : ''),
}
})
},
},
}

View File

@ -1,6 +1,5 @@
import { shallowMount } from '@vue/test-utils'
import Creation from './Creation.vue'
import Vue from 'vue'
const localVue = global.localVue
@ -29,25 +28,23 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
})
const toastErrorMock = jest.fn()
const storeCommitMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
$apollo: {
query: apolloQueryMock,
},
$toasted: {
error: toastErrorMock,
},
$moment: jest.fn(() => {
return {
format: jest.fn((m) => m),
subtract: jest.fn(() => {
return {
format: jest.fn((m) => m),
}
}),
}
}),
$store: {
commit: storeCommitMock,
state: {
userSelectedInMassCreation: [],
},
},
}
describe('Creation', () => {
@ -59,6 +56,7 @@ describe('Creation', () => {
describe('shallowMount', () => {
beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper()
})
@ -68,7 +66,15 @@ describe('Creation', () => {
describe('apollo returns user array', () => {
it('calls the searchUser query', () => {
expect(apolloQueryMock).toBeCalled()
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
currentPage: 1,
pageSize: 25,
},
}),
)
})
it('sets the data of itemsList', () => {
@ -93,121 +99,33 @@ describe('Creation', () => {
})
})
describe('update item', () => {
describe('push item', () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe('push', () => {
beforeEach(() => {
wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
'update-item',
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
'push',
)
})
it('removes the pushed item from itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
})
it('adds the pushed item to itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
])
})
describe('remove', () => {
beforeEach(() => {
wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
'update-item',
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
'remove',
)
})
it('removes the item from itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([])
})
it('adds the item to itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
])
})
wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
})
describe('error', () => {
const consoleErrorMock = jest.fn()
const warnHandler = Vue.config.warnHandler
beforeEach(() => {
Vue.config.warnHandler = (w) => {}
// eslint-disable-next-line no-console
console.error = consoleErrorMock
wrapper.findComponent({ name: 'UserTable' }).vm.$emit('update-item', {}, 'no-rule')
})
afterEach(() => {
Vue.config.warnHandler = warnHandler
})
it('throws an error', () => {
expect(consoleErrorMock).toBeCalledWith(expect.objectContaining({ message: 'no-rule' }))
})
it('removes the pushed item from itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
})
})
describe('remove all bookmarks', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
'update-item',
it('adds the pushed item to itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([
{
userId: 2,
firstName: 'Benjamin',
@ -216,8 +134,87 @@ describe('Creation', () => {
creation: [800, 600, 400],
showDetails: false,
},
'push',
)
])
})
it('updates userSelectedInMassCreation in store', () => {
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
])
})
})
describe('remove item', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
await wrapper
.findAllComponents({ name: 'UserTable' })
.at(1)
.vm.$emit('remove-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
})
it('adds the removed item to itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
})
it('removes the item from itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([])
})
it('commits empty array as userSelectedInMassCreation', () => {
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
})
})
describe('remove all bookmarks', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
jest.clearAllMocks()
wrapper.findComponent({ name: 'CreationFormular' }).vm.$emit('remove-all-bookmark')
})
@ -225,8 +222,41 @@ describe('Creation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([])
})
it('adds all items to itemsList', () => {
expect(wrapper.vm.itemsList).toHaveLength(2)
it('commits empty array to userSelectedInMassCreation', () => {
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
})
it('calls searchUsers', () => {
expect(apolloQueryMock).toBeCalled()
})
})
describe('store has items in userSelectedInMassCreation', () => {
beforeEach(() => {
mocks.$store.state.userSelectedInMassCreation = [
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
]
wrapper = Wrapper()
})
it('has only one item itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
})
})
@ -237,12 +267,28 @@ describe('Creation', () => {
it('calls API when criteria changes', async () => {
await wrapper.setData({ criteria: 'XX' })
expect(apolloQueryMock).toBeCalled()
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: 'XX',
currentPage: 1,
pageSize: 25,
},
}),
)
})
it('calls API when currentPage changes', async () => {
await wrapper.setData({ currentPage: 2 })
expect(apolloQueryMock).toBeCalled()
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
currentPage: 2,
pageSize: 25,
},
}),
)
})
})

View File

@ -16,7 +16,7 @@
:fieldsTable="Searchfields"
:criteria="criteria"
:creation="creation"
@update-item="updateItem"
@push-item="pushItem"
/>
<b-pagination
pills
@ -35,7 +35,7 @@
:fieldsTable="fields"
:criteria="null"
:creation="creation"
@update-item="updateItem"
@remove-item="removeItem"
/>
<div v-if="itemsMassCreation.length === 0">
{{ $t('multiple_creation_text') }}
@ -65,52 +65,15 @@ export default {
data() {
return {
showArrays: false,
Searchfields: [
{ key: 'bookmark', label: 'bookmark' },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
// label: this.$t('open_creation') + 'Jan | Feb | März',
label:
this.$moment().subtract(2, 'month').format('MMM') +
' | ' +
this.$moment().subtract(1, 'month').format('MMM') +
' | ' +
this.$moment().format('MMM'),
formatter: (value, key, item) => {
return String(value[0]) + ` | ` + String(value[1]) + ` | ` + String(value[2])
},
},
{ key: 'email', label: this.$t('e_mail') },
],
fields: [
{ key: 'email', label: this.$t('e_mail') },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
// label: this.$t('open_creation') + 'Jan | Feb | März',
label:
this.$moment().subtract(2, 'month').format('MMM') +
' | ' +
this.$moment().subtract(1, 'month').format('MMM') +
' | ' +
this.$moment().format('MMM'),
formatter: (value, key, item) => {
return String(value[0]) + ` | ` + String(value[1]) + ` | ` + String(value[2])
},
},
{ key: 'bookmark', label: this.$t('remove') },
],
itemsList: [],
itemsMassCreation: [],
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
radioSelectedMass: '',
criteria: '',
creation: [null, null, null],
rows: 0,
currentPage: 1,
perPage: 25,
now: Date.now(),
}
},
async created() {
@ -136,37 +99,81 @@ export default {
showDetails: false,
}
})
if (this.itemsMassCreation.length !== 0) {
const selectedIndices = this.itemsMassCreation.map((item) => item.userId)
this.itemsList = this.itemsList.filter((item) => !selectedIndices.includes(item.userId))
}
})
.catch((error) => {
this.$toasted.error(error.message)
})
},
updateItem(e, event) {
let index = 0
let findArr = {}
switch (event) {
case 'push':
findArr = this.itemsList.find((item) => e.userId === item.userId)
index = this.itemsList.indexOf(findArr)
this.itemsList.splice(index, 1)
this.itemsMassCreation.push(findArr)
break
case 'remove':
findArr = this.itemsMassCreation.find((item) => e.userId === item.userId)
index = this.itemsMassCreation.indexOf(findArr)
this.itemsMassCreation.splice(index, 1)
this.itemsList.push(findArr)
break
default:
throw new Error(event)
}
pushItem(selectedItem) {
this.itemsMassCreation = [
this.itemsList.find((item) => selectedItem.userId === item.userId),
...this.itemsMassCreation,
]
this.itemsList = this.itemsList.filter((item) => selectedItem.userId !== item.userId)
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
},
removeItem(selectedItem) {
this.itemsList = [
this.itemsMassCreation.find((item) => selectedItem.userId === item.userId),
...this.itemsList,
]
this.itemsMassCreation = this.itemsMassCreation.filter(
(item) => selectedItem.userId !== item.userId,
)
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
},
removeAllBookmark() {
this.itemsMassCreation = []
this.$store.commit('setUserSelectedInMassCreation', [])
this.getUsers()
},
},
computed: {
Searchfields() {
return [
{ key: 'bookmark', label: 'bookmark' },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
label: this.creationLabel,
formatter: (value, key, item) => {
return value.join(' | ')
},
},
{ key: 'email', label: this.$t('e_mail') },
]
},
fields() {
return [
{ key: 'email', label: this.$t('e_mail') },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
label: this.creationLabel,
formatter: (value, key, item) => {
return value.join(' | ')
},
},
{ key: 'bookmark', label: this.$t('remove') },
]
},
creationLabel() {
const now = new Date(this.now)
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1)
const beforeLastMonth = new Date(now.getFullYear(), now.getMonth() - 2, 1)
return [
this.$d(beforeLastMonth, 'monthShort'),
this.$d(lastMonth, 'monthShort'),
this.$d(now, 'monthShort'),
].join(' | ')
},
},
watch: {
currentPage() {
this.getUsers()

View File

@ -39,6 +39,7 @@ const apolloMutateMock = jest.fn().mockResolvedValue({})
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
$store: {
commit: storeCommitMock,
},
@ -50,11 +51,6 @@ const mocks = {
error: toastedErrorMock,
success: toastedSuccessMock,
},
$moment: jest.fn((value) => {
return {
format: jest.fn((format) => value),
}
}),
}
describe('CreationConfirm', () => {

View File

@ -96,9 +96,9 @@ export default {
{ key: 'memo', label: 'Text' },
{
key: 'date',
label: 'Datum',
label: this.$t('date'),
formatter: (value) => {
return this.$moment(value).format('ll')
return this.$d(new Date(value), 'short')
},
},
{ key: 'moderator', label: 'Moderator' },

View File

@ -24,22 +24,13 @@ const toastErrorMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
$apollo: {
query: apolloQueryMock,
},
$toasted: {
error: toastErrorMock,
},
$moment: jest.fn(() => {
return {
format: jest.fn((m) => m),
subtract: jest.fn(() => {
return {
format: jest.fn((m) => m),
}
}),
}
}),
}
describe('UserSearch', () => {

View File

@ -44,41 +44,14 @@ export default {
data() {
return {
showArrays: false,
fields: [
{ key: 'email', label: this.$t('e_mail') },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
label: [
this.$moment().subtract(2, 'month').format('MMM'),
this.$moment().subtract(1, 'month').format('MMM'),
this.$moment().format('MMM'),
].join(' | '),
formatter: (value, key, item) => {
return value.join(' | ')
},
},
{ key: 'show_details', label: this.$t('details') },
{ key: 'confirm_mail', label: this.$t('confirmed') },
{ key: 'transactions_list', label: this.$t('transaction') },
],
searchResult: [],
massCreation: [],
criteria: '',
currentMonth: {
short: this.$moment().format('MMMM'),
},
lastMonth: {
short: this.$moment().subtract(1, 'month').format('MMMM'),
},
beforeLastMonth: {
short: this.$moment().subtract(2, 'month').format('MMMM'),
},
filterCheckedEmails: false,
rows: 0,
currentPage: 1,
perPage: 25,
now: Date.now(),
}
},
methods: {
@ -111,6 +84,37 @@ export default {
this.getUsers()
},
},
computed: {
lastMonthDate() {
const now = new Date(this.now)
return new Date(now.getFullYear(), now.getMonth() - 1, 1)
},
beforeLastMonthDate() {
const now = new Date(this.now)
return new Date(now.getFullYear(), now.getMonth() - 2, 1)
},
fields() {
return [
{ key: 'email', label: this.$t('e_mail') },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
label: [
this.$d(this.beforeLastMonthDate, 'monthShort'),
this.$d(this.lastMonthDate, 'monthShort'),
this.$d(this.now, 'monthShort'),
].join(' | '),
formatter: (value, key, item) => {
return value.join(' | ')
},
},
{ key: 'show_details', label: this.$t('details') },
{ key: 'confirm_mail', label: this.$t('confirmed') },
{ key: 'transactions_list', label: this.$t('transaction') },
]
},
},
created() {
this.getUsers()
},

View File

@ -24,6 +24,9 @@ export const mutations = {
moderator: (state, moderator) => {
state.moderator = moderator
},
setUserSelectedInMassCreation: (state, userSelectedInMassCreation) => {
state.userSelectedInMassCreation = userSelectedInMassCreation
},
}
export const actions = {
@ -44,6 +47,7 @@ const store = new Vuex.Store({
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
moderator: null,
openCreations: 0,
userSelectedInMassCreation: [],
},
// Syncronous mutation of the state
mutations,

View File

@ -10,6 +10,7 @@ const {
resetOpenCreations,
setOpenCreations,
moderator,
setUserSelectedInMassCreation,
} = mutations
const { logout } = actions
@ -64,6 +65,14 @@ describe('Vuex store', () => {
expect(state.openCreations).toEqual(12)
})
})
describe('setUserSelectedInMassCreation', () => {
it('sets userSelectedInMassCreation to given value', () => {
const state = { userSelectedInMassCreation: [] }
setUserSelectedInMassCreation(state, [0, 1, 2])
expect(state.userSelectedInMassCreation).toEqual([0, 1, 2])
})
})
})
describe('actions', () => {

View File

@ -9032,11 +9032,6 @@ mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
moment@^2.19.2, moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -12491,13 +12486,6 @@ vue-loader@^15.9.2:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-moment@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vue-moment/-/vue-moment-4.1.0.tgz#092a8ff723a96c6f85a0a8e23ad30f0bf320f3b0"
integrity sha512-Gzisqpg82ItlrUyiD9d0Kfru+JorW2o4mQOH06lEDZNgxci0tv/fua1Hl0bo4DozDV2JK1r52Atn/8QVCu8qQw==
dependencies:
moment "^2.19.2"
vue-router@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.3.tgz#041048053e336829d05dafacf6a8fb669a2e7999"

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config()
const constants = {
DB_VERSION: '0012-login_user_backups_unify_wordlist',
DB_VERSION: '0016-transaction_signatures',
}
const server = {

View File

@ -10,7 +10,6 @@ import { UpdatePendingCreation } from '../model/UpdatePendingCreation'
import { RIGHTS } from '../../auth/RIGHTS'
import { TransactionRepository } from '../../typeorm/repository/Transaction'
import { TransactionCreationRepository } from '../../typeorm/repository/TransactionCreation'
import { LoginPendingTasksAdminRepository } from '../../typeorm/repository/LoginPendingTasksAdmin'
import { UserRepository } from '../../typeorm/repository/User'
import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs'
@ -23,7 +22,7 @@ import { UserTransactionRepository } from '../../typeorm/repository/UserTransact
import { BalanceRepository } from '../../typeorm/repository/Balance'
import { calculateDecay } from '../../util/decay'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { LoginPendingTasksAdmin } from '@entity/LoginPendingTasksAdmin'
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
@Resolver()
export class AdminResolver {
@ -68,16 +67,15 @@ export class AdminResolver {
const creations = await getUserCreations(user.id)
const creationDateObj = new Date(creationDate)
if (isCreationValid(creations, amount, creationDateObj)) {
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const loginPendingTaskAdmin = loginPendingTasksAdminRepository.create()
loginPendingTaskAdmin.userId = user.id
loginPendingTaskAdmin.amount = BigInt(amount * 10000)
loginPendingTaskAdmin.created = new Date()
loginPendingTaskAdmin.date = creationDateObj
loginPendingTaskAdmin.memo = memo
loginPendingTaskAdmin.moderator = moderator
const adminPendingCreation = AdminPendingCreation.create()
adminPendingCreation.userId = user.id
adminPendingCreation.amount = BigInt(amount * 10000)
adminPendingCreation.created = new Date()
adminPendingCreation.date = creationDateObj
adminPendingCreation.memo = memo
adminPendingCreation.moderator = moderator
await loginPendingTasksAdminRepository.save(loginPendingTaskAdmin)
await AdminPendingCreation.save(adminPendingCreation)
}
return getUserCreations(user.id)
}
@ -116,8 +114,7 @@ export class AdminResolver {
const userRepository = getCustomRepository(UserRepository)
const user = await userRepository.findByEmail(email)
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const pendingCreationToUpdate = await loginPendingTasksAdminRepository.findOneOrFail({ id })
const pendingCreationToUpdate = await AdminPendingCreation.findOneOrFail({ id })
if (pendingCreationToUpdate.userId !== user.id) {
throw new Error('user of the pending creation and send user does not correspond')
@ -137,7 +134,7 @@ export class AdminResolver {
pendingCreationToUpdate.date = new Date(creationDate)
pendingCreationToUpdate.moderator = moderator
await loginPendingTasksAdminRepository.save(pendingCreationToUpdate)
await AdminPendingCreation.save(pendingCreationToUpdate)
const result = new UpdatePendingCreation()
result.amount = parseInt(amount.toString())
result.memo = pendingCreationToUpdate.memo
@ -151,8 +148,7 @@ export class AdminResolver {
@Authorized([RIGHTS.SEARCH_PENDING_CREATION])
@Query(() => [PendingCreation])
async getPendingCreations(): Promise<PendingCreation[]> {
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const pendingCreations = await loginPendingTasksAdminRepository.find()
const pendingCreations = await AdminPendingCreation.find()
const pendingCreationsPromise = await Promise.all(
pendingCreations.map(async (pendingCreation) => {
@ -179,18 +175,15 @@ export class AdminResolver {
@Authorized([RIGHTS.DELETE_PENDING_CREATION])
@Mutation(() => Boolean)
async deletePendingCreation(@Arg('id') id: number): Promise<boolean> {
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const entity = await loginPendingTasksAdminRepository.findOneOrFail(id)
const res = await loginPendingTasksAdminRepository.delete(entity)
const entity = await AdminPendingCreation.findOneOrFail(id)
const res = await AdminPendingCreation.delete(entity)
return !!res
}
@Authorized([RIGHTS.CONFIRM_PENDING_CREATION])
@Mutation(() => Boolean)
async confirmPendingCreation(@Arg('id') id: number, @Ctx() context: any): Promise<boolean> {
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const pendingCreation = await loginPendingTasksAdminRepository.findOneOrFail(id)
const pendingCreation = await AdminPendingCreation.findOneOrFail(id)
const userRepository = getCustomRepository(UserRepository)
const moderatorUser = await userRepository.findByPubkeyHex(context.pubKey)
if (moderatorUser.id === pendingCreation.userId)
@ -202,7 +195,6 @@ export class AdminResolver {
transaction.transactionTypeId = 1
transaction.memo = pendingCreation.memo
transaction.received = receivedCallDate
transaction.blockchainTypeId = 1
transaction = await transactionRepository.save(transaction)
if (!transaction) throw new Error('Could not create transaction')
@ -251,7 +243,7 @@ export class AdminResolver {
userBalance.modified = receivedCallDate
userBalance.recordDate = receivedCallDate
await balanceRepository.save(userBalance)
await loginPendingTasksAdminRepository.delete(pendingCreation)
await AdminPendingCreation.delete(pendingCreation)
return true
}
@ -280,12 +272,12 @@ async function getUserCreations(id: number): Promise<number[]> {
.orderBy('target_month', 'ASC')
.getRawMany()
const loginPendingTasksAdminRepository = getCustomRepository(LoginPendingTasksAdminRepository)
const pendingAmountsQuery = await loginPendingTasksAdminRepository
.createQueryBuilder('login_pending_tasks_admin')
.select('MONTH(login_pending_tasks_admin.date)', 'target_month')
.addSelect('SUM(login_pending_tasks_admin.amount)', 'sum')
.where('login_pending_tasks_admin.userId = :id', { id })
const pendingAmountsQuery = await AdminPendingCreation.createQueryBuilder(
'admin_pending_creations',
)
.select('MONTH(admin_pending_creations.date)', 'target_month')
.addSelect('SUM(admin_pending_creations.amount)', 'sum')
.where('admin_pending_creations.userId = :id', { id })
.andWhere({
date: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, {
date: dateBeforeLastMonth,
@ -336,7 +328,7 @@ async function getUserCreations(id: number): Promise<number[]> {
]
}
function updateCreations(creations: number[], pendingCreation: LoginPendingTasksAdmin): number[] {
function updateCreations(creations: number[], pendingCreation: AdminPendingCreation): number[] {
const dateMonth = moment().format('YYYY-MM')
const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM')
const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM')

View File

@ -36,172 +36,6 @@ import { hasUserAmount, isHexPublicKey } from '../../util/validate'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { RIGHTS } from '../../auth/RIGHTS'
/*
# Test
## Prepare
> sudo systemctl start docker
> docker-compose up mariadb
> DROP all databases
> docker-compose down
> docker compose up mariadb database
> verify there is exactly one database `gradido_community`
TODO:
INSERT INTO `login_groups` (`id`, `alias`, `name`, `url`, `host`, `home`, `description`) VALUES
(1, 'docker', 'docker gradido group', 'localhost', 'nginx', '/', 'gradido test group for docker and stage2 with blockchain db');
>> Database is cool
### Start login server
> docker-compose up login-server community-server nginx
>> Login & community servers and nginx proxy are up and running
## Build database
> cd database
> yarn
> yarn build
> cd ..
>> Database has been built successful
### Start backend (no docker for debugging)
> cd backend
> yarn
> yarn dev
>> Backend is up and running
### Create users
> chromium http://localhost:4000/graphql
> mutation{createUser(email: "receiver@user.net", firstName: "Receiver", lastName: "user", password: "123!AAAb", language: "de")}
> mutation{createUser(email: "sender@user.net", firstName: "Sender", lastName: "user", password: "123!AAAb", language: "de")}
> mutation{createUser(email: "creator@user.net", firstName: "Creator", lastName: "user", password: "123!AAAb", language: "de")}
>> Verify you have 3 entries in `login_users`, `login_user_backups` and `state_users`
### make creator an admin
> INSERT INTO login_user_roles (id, user_id, role_id) VALUES (NULL, '3', '1');
> UPDATE login_users SET email_checked = 1 WHERE id = 3;
> uncomment line: 19 in community_server/src/Controller/ServerUsersController.php
> chromium http://localhost/server-users/add
> create user `creator` `123` `creator@different.net`
>> verify you have 1 entry in `server_users`
> login with user on http://localhost/server-users
> activate server user by changing the corresponding flag in the interface
> navigate to http://localhost/transaction-creations/create-multi
> create 1000GDD for user sender@user.net
> navigate to http://localhost
> login with `creator@user.net` `123!AAAb`
> confirm transaction (top right corner - click the thingy, click the green button `Transaktion abschließen`)
### the test:
> chromium http://localhost:4000/graphql
> query{login(email: "sender@user.net", password: "123!AAAb"){pubkey}}
>> copy token from network tab (inspect)
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
> Headers: {"Authorization": "Bearer ${token}"}
>> Verify via Database that stuff is as it should see `state_balance` & `transaction_send_coins`
### create decay block
> chromium http://localhost/transactions/add
> login with `creator` `123`
> select `decay start`
> press submit
> wait for at least 0.02 display of decay on user sender@user.net on old frontend, this should be aprox 10min
> chromium http://localhost:4000/graphql
> query{login(email: "sender@user.net", password: "123!AAAb"){pubkey}}
>> copy token from network tab (inspect)
> mutation{sendCoins(email: "receiver@user.net", amount: 10.0, memo: "Hier!")}
>> verify in `transaction_send_coins` that a decay was taken into account
>> same in `state_balances`
>> now check the old frontend
>>> sender@user.net should have a decay of 0.02
>>> while receiver@user.net should have zero decay on anything (old frontend)
### Export data
> docker-compose up phpmyadmin
> chromium http://localhost:8074/
> select gradido_community
> export
> select custom
> untick structure
> ok
## Results
NOTE: We decided not to write the `transaction_signatures` since its unused. This is the main difference.
NOTE: We fixed a bug in the `state_user_transactions code` with the new implementation of apollo
Master:
--
-- Dumping data for table `state_user_transactions`
--
INSERT INTO `state_user_transactions` (`id`, `state_user_id`, `transaction_id`, `transaction_type_id`, `balance`, `balance_date`) VALUES
(1, 2, 1, 1, 10000000, '2021-11-05 12:45:18'),
(2, 2, 2, 2, 9900000, '2021-11-05 12:48:35'),
(3, 1, 2, 2, 100000, '2021-11-05 12:48:35'),
(4, 2, 3, 2, 9800000, '2021-11-05 12:49:07'),
(5, 1, 3, 2, 200000, '2021-11-05 12:49:07'),
(6, 2, 5, 2, 9699845, '2021-11-05 13:03:50'),
(7, 1, 5, 2, 99996, '2021-11-05 13:03:50');
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `state_group_id`, `transaction_type_id`, `tx_hash`, `memo`, `received`, `blockchain_type_id`) VALUES
(1, NULL, 1, 0x9ccdcd01ccb6320c09c2d1da2f0bf735a95ece0e7c1df6bbff51918fbaec061700000000000000000000000000000000, '', '2021-11-05 12:45:18', 1),
(2, NULL, 2, 0x58d7706a67fa4ff4b8038168c6be39a2963d7e28e9d3872759ad09c519fe093700000000000000000000000000000000, 'Hier!', '2021-11-05 12:48:35', 1),
(3, NULL, 2, 0x427cd214f92ef35af671129d50edc5a478c53d1e464f285b7615d9794a69f69b00000000000000000000000000000000, 'Hier!', '2021-11-05 12:49:07', 1),
(4, NULL, 9, 0x32807368f0906a21b94c072599795bc9eeab88fb565df82e85cc62a4fdcde48500000000000000000000000000000000, '', '2021-11-05 12:51:51', 1),
(5, NULL, 2, 0x75eb729e0f60a1c8cead1342955853d2440d7a2ea57dfef6d4a18bff0d94491e00000000000000000000000000000000, 'Hier!', '2021-11-05 13:03:50', 1);
--
-- Dumping data for table `transaction_signatures`
--
INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubkey`) VALUES
(1, 1, 0x5888edcdcf77aaadad6d321882903bc831d7416f17213fd5020a764365b5fcb336e4c7917385a1278ea44ccdb31eac4a09e448053b5e3f8f1fe5da3baf53c008, 0xd5b20f8dee415038bfa2b6b0e1b40ff54850351109444863b04d6d28825b7b7d),
(2, 2, 0xf6fef428f8f22faf7090f7d740e6088d1d90c58ae92d757117d7d91d799e659f3a3a0c65a3fd97cbde798e761f9d23eff13e8810779a184c97c411f28e7c4608, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a),
(3, 3, 0x8ebe9730c6cf61f56ef401d6f2bd229f3c298ca3c2791ee9137e4827b7af6c6d6566fca616eb1fe7adc2e4d56b5c7350ae3990c9905580630fa75ecffca8e001, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a),
(4, 5, 0x50cf418f7e217391e89ab9c2879ae68d7c7c597d846b4fe1c082b5b16e5d0c85c328fbf48ad3490bcfe94f446700ae0a4b0190e76d26cc752abced58f480c80f, 0xdc74a589004377ab14836dce68ce2ca34e5b17147cd78ad4b3afe8137524ae8a);
This Feature Branch:
--
-- Dumping data for table `state_user_transactions`
--
INSERT INTO `state_user_transactions` (`id`, `state_user_id`, `transaction_id`, `transaction_type_id`, `balance`, `balance_date`) VALUES
(1, 2, 1, 1, 10000000, '2021-11-05 00:25:46'),
(12, 2, 7, 2, 9900000, '2021-11-05 00:55:37'),
(13, 1, 7, 2, 100000, '2021-11-05 00:55:37'),
(14, 2, 8, 2, 9800000, '2021-11-05 01:00:04'),
(15, 1, 8, 2, 200000, '2021-11-05 01:00:04'),
(16, 2, 10, 2, 9699772, '2021-11-05 01:17:41'),
(17, 1, 10, 2, 299995, '2021-11-05 01:17:41');
--
-- Dumping data for table `transactions`
--
INSERT INTO `transactions` (`id`, `state_group_id`, `transaction_type_id`, `tx_hash`, `memo`, `received`, `blockchain_type_id`) VALUES
(1, NULL, 1, 0xdd030d475479877587d927ed9024784ba62266cf1f3d87862fc98ad68f7b26e400000000000000000000000000000000, '', '2021-11-05 00:25:46', 1),
(7, NULL, 2, NULL, 'Hier!', '2021-11-05 00:55:37', 1),
(8, NULL, 2, NULL, 'Hier!', '2021-11-05 01:00:04', 1),
(9, NULL, 9, 0xb1cbedbf126aa35f5edbf06e181c415361d05228ab4da9d19a4595285a673dfa00000000000000000000000000000000, '', '2021-11-05 01:05:34', 1),
(10, NULL, 2, NULL, 'Hier!', '2021-11-05 01:17:41', 1);
--
-- Dumping data for table `transaction_signatures`
--
INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubkey`) VALUES
(1, 1, 0x60d632479707e5d01cdc32c3326b5a5bae11173a0c06b719ee7b552f9fd644de1a0cd4afc207253329081d39dac1a63421f51571d836995c649fc39afac7480a, 0x48c45cb4fea925e83850f68f2fa8f27a1a4ed1bcba68cdb59fcd86adef3f52ee);
*/
// Helper function
async function calculateAndAddDecayTransactions(
userTransactions: dbUserTransaction[],

View File

@ -1,5 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { LoginPendingTasksAdmin } from '@entity/LoginPendingTasksAdmin'
@EntityRepository(LoginPendingTasksAdmin)
export class LoginPendingTasksAdminRepository extends Repository<LoginPendingTasksAdmin> {}

View File

@ -1,25 +1,34 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
import { TransactionCreation } from './TransactionCreation'
import { TransactionSendCoin } from './TransactionSendCoin'
import { TransactionCreation } from '../TransactionCreation'
import { TransactionSendCoin } from '../TransactionSendCoin'
@Entity('transactions')
export class Transaction extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column({ name: 'transaction_type_id' })
@Column({ name: 'state_group_id', unsigned: true, default: null })
stateGroupId: number
@Column({ name: 'transaction_type_id', unsigned: true, nullable: false })
transactionTypeId: number
@Column({ name: 'tx_hash', type: 'binary', length: 48 })
@Column({ name: 'tx_hash', type: 'binary', length: 48, default: null })
txHash: Buffer
@Column()
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
memo: string
@Column({ type: 'timestamp' })
@Column({ type: 'timestamp', nullable: false, default: () => 'CURRENT_TIMESTAMP' })
received: Date
@Column({ name: 'blockchain_type_id' })
@Column({
name: 'blockchain_type_id',
type: 'bigint',
unsigned: true,
nullable: false,
default: 1,
})
blockchainTypeId: number
@OneToOne(() => TransactionSendCoin, (transactionSendCoin) => transactionSendCoin.transaction)

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
import { Transaction } from './Transaction'
import { Transaction } from '../Transaction'
@Entity('transaction_creations')
export class TransactionCreation extends BaseEntity {

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
import { Transaction } from './Transaction'
import { Transaction } from '../Transaction'
@Entity('transaction_send_coins')
export class TransactionSendCoin extends BaseEntity {

View File

@ -1,21 +1,21 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'
import { Transaction } from './Transaction'
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
import { Transaction } from '../Transaction'
@Entity('transaction_signatures')
export class TransactionSignature extends BaseEntity {
@PrimaryGeneratedColumn()
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'transaction_id' })
transactionId: number
@Column({ type: 'binary', length: 64 })
@Column({ type: 'binary', length: 64, nullable: false })
signature: Buffer
@Column({ type: 'binary', length: 32 })
@Column({ type: 'binary', length: 32, nullable: false })
pubkey: Buffer
@ManyToOne(() => Transaction)
@OneToOne(() => Transaction)
@JoinColumn({ name: 'transaction_id' })
transaction: Transaction
}

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from './UserSetting'
import { UserSetting } from '../UserSetting'
// Moriz: I do not like the idea of having two user tables
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'
import { User } from './User'
import { User } from '../User'
@Entity()
export class UserSetting extends BaseEntity {

View File

@ -0,0 +1,25 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity('admin_pending_creations')
export class AdminPendingCreation extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ unsigned: true, nullable: false })
userId: number
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
created: Date
@Column({ type: 'datetime', nullable: false })
date: Date
@Column({ length: 256, nullable: true, default: null })
memo: string
@Column({ type: 'bigint', nullable: false })
amount: BigInt
@Column()
moderator: number
}

View File

@ -0,0 +1,34 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
import { TransactionCreation } from '../TransactionCreation'
import { TransactionSendCoin } from '../TransactionSendCoin'
@Entity('transactions')
export class Transaction extends BaseEntity {
// TODO the id is defined as bigint(20) - there might be problems with that: https://github.com/typeorm/typeorm/issues/2400
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'transaction_type_id', unsigned: true, nullable: false })
transactionTypeId: number
@Column({ name: 'tx_hash', type: 'binary', length: 48, default: null })
txHash: Buffer
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
memo: string
@Column({ type: 'timestamp', nullable: false, default: () => 'CURRENT_TIMESTAMP' })
received: Date
@Column({ type: 'binary', length: 64, nullable: true, default: null })
signature: Buffer
@Column({ type: 'binary', length: 32, nullable: true, default: null })
pubkey: Buffer
@OneToOne(() => TransactionSendCoin, (transactionSendCoin) => transactionSendCoin.transaction)
transactionSendCoin: TransactionSendCoin
@OneToOne(() => TransactionCreation, (transactionCreation) => transactionCreation.transaction)
transactionCreation: TransactionCreation
}

View File

@ -0,0 +1 @@
export { AdminPendingCreation } from './0015-admin_pending_creations/AdminPendingCreation'

View File

@ -1 +0,0 @@
export { LoginPendingTasksAdmin } from './0005-admin_tables/LoginPendingTasksAdmin'

View File

@ -1 +0,0 @@
export { LoginUserRoles } from './0003-login_server_tables/LoginUserRoles'

View File

@ -1 +1 @@
export { Transaction } from './0001-init_db/Transaction'
export { Transaction } from './0016-transaction_signatures/Transaction'

View File

@ -1 +0,0 @@
export { TransactionSignature } from './0001-init_db/TransactionSignature'

View File

@ -2,34 +2,30 @@ import { Balance } from './Balance'
import { LoginElopageBuys } from './LoginElopageBuys'
import { LoginEmailOptIn } from './LoginEmailOptIn'
import { LoginUser } from './LoginUser'
import { LoginUserRoles } from './LoginUserRoles'
import { LoginUserBackup } from './LoginUserBackup'
import { Migration } from './Migration'
import { ServerUser } from './ServerUser'
import { Transaction } from './Transaction'
import { TransactionCreation } from './TransactionCreation'
import { TransactionSignature } from './TransactionSignature'
import { TransactionSendCoin } from './TransactionSendCoin'
import { User } from './User'
import { UserSetting } from './UserSetting'
import { UserTransaction } from './UserTransaction'
import { LoginPendingTasksAdmin } from './LoginPendingTasksAdmin'
import { AdminPendingCreation } from './AdminPendingCreation'
export const entities = [
AdminPendingCreation,
Balance,
LoginElopageBuys,
LoginEmailOptIn,
LoginUser,
LoginUserRoles,
LoginUserBackup,
Migration,
ServerUser,
Transaction,
TransactionCreation,
TransactionSignature,
TransactionSendCoin,
User,
UserSetting,
UserTransaction,
LoginPendingTasksAdmin,
]

View File

@ -0,0 +1,142 @@
/* MIGRATION TO DROP UNUSED TABLES
*
* This migration removes all tables without data and entity definition.
* Base for evaluation are the production data from 27.01.2022 which had 40 tables present
* The migration reduces the amount of tables to 28
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`DROP TABLE \`login_app_access_tokens\`;`)
await queryFn(`DROP TABLE \`pending_transactions\`;`)
await queryFn(`DROP TABLE \`roles\`;`)
await queryFn(`DROP TABLE \`state_created\`;`)
await queryFn(`DROP TABLE \`state_groups\`;`)
await queryFn(`DROP TABLE \`state_group_addresses\`;`)
await queryFn(`DROP TABLE \`state_group_relationships\`;`)
await queryFn(`DROP TABLE \`state_relationship_types\`;`)
await queryFn(`DROP TABLE \`state_user_roles\`;`)
await queryFn(`DROP TABLE \`transaction_group_addaddress\`;`)
await queryFn(`DROP TABLE \`transaction_group_allowtrades\`;`)
await queryFn(`DROP TABLE \`transaction_group_creates\`;`)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE IF NOT EXISTS \`login_app_access_tokens\` (
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int NOT NULL,
\`access_code\` bigint unsigned NOT NULL,
\`created\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`access_code\` (\`access_code\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
CREATE TABLE \`pending_transactions\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transactionID\` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL,
\`service\` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
\`method\` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
\`h_server_id\` int(11) NOT NULL,
\`timeout\` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (\`id\`),
UNIQUE KEY \`transactionID\` (\`transactionID\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`roles\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`title\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_created\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_id\` int(10) unsigned NOT NULL,
\`month\` tinyint(3) unsigned NOT NULL,
\`year\` smallint(5) unsigned NOT NULL,
\`state_user_id\` int(10) unsigned NOT NULL,
\`created\` datetime NOT NULL,
\`short_ident_hash\` int(10) unsigned NOT NULL,
PRIMARY KEY (\`id\`),
KEY \`short_ident_hash\` (\`short_ident_hash\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_groups\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`index_id\` varbinary(64) NOT NULL,
\`name\` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
\`root_public_key\` binary(32) NOT NULL,
\`user_count\` smallint(5) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_group_addresses\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`group_id\` int(10) unsigned NOT NULL,
\`public_key\` binary(32) NOT NULL,
\`address_type_id\` int(10) unsigned NOT NULL,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`public_key\` (\`public_key\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_group_relationships\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`group1_id\` int(10) unsigned NOT NULL,
\`group2_id\` int(10) unsigned NOT NULL,
\`state_relationship_id\` int(10) unsigned NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_relationship_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL,
\`text\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`state_user_roles\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`state_user_id\` int(11) NOT NULL,
\`role_id\` int(11) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`transaction_group_addaddress\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_id\` int(10) unsigned NOT NULL,
\`address_type_id\` int(10) unsigned NOT NULL,
\`remove_from_group\` tinyint(1) DEFAULT 0,
\`public_key\` binary(32) NOT NULL,
\`state_user_id\` int(10) unsigned NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`transaction_group_allowtrades\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_id\` int(10) unsigned NOT NULL,
\`remote_group_id\` varbinary(64) NOT NULL,
\`allow\` tinyint(4) NOT NULL DEFAULT 0,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
CREATE TABLE \`transaction_group_creates\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_id\` int(10) unsigned NOT NULL,
\`group_public_key\` binary(32) NOT NULL,
\`group_id\` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
\`name\` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
}

View File

@ -0,0 +1,351 @@
/* MIGRATION TO DROP UNUSED TABLES
*
* This migration removes all tables with static or unused data and entity definition.
* Base for evaluation are the production data from 27.01.2022 which had 28 tables present
* The migration reduces the amount of tables to 16
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`DROP TABLE \`address_types\`;`)
await queryFn(`DROP TABLE \`admin_errors\`;`)
await queryFn(`DROP TABLE \`blockchain_types\`;`)
await queryFn(`DROP TABLE \`community_profiles\`;`)
await queryFn(`DROP TABLE \`login_email_opt_in_types\`;`)
await queryFn(`DROP TABLE \`login_groups\`;`)
await queryFn(`DROP TABLE \`login_roles\`;`)
await queryFn(`DROP TABLE \`login_user_roles\`;`)
await queryFn(`DROP TABLE \`operators\`;`)
await queryFn(`DROP TABLE \`operator_types\`;`)
await queryFn(`DROP TABLE \`state_errors\`;`)
await queryFn(`DROP TABLE \`transaction_types\`;`)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE \`address_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
\`text\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`address_types\` VALUES
(1,'user main','user main address');
`)
await queryFn(`
CREATE TABLE \`admin_errors\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`state_user_id\` int(11) NOT NULL,
\`controller\` varchar(255) NOT NULL,
\`action\` varchar(255) NOT NULL,
\`state\` varchar(255) NOT NULL,
\`msg\` varchar(255) NOT NULL,
\`details\` varchar(255) DEFAULT NULL,
\`created\` datetime NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=155 DEFAULT CHARSET=utf8mb4;
`)
// NOTE: This data is no longer generated
await queryFn(`
INSERT INTO \`admin_errors\` VALUES
(54,272,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2020-11-04 17:57:07'),
(55,272,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2020-11-04 17:57:16'),
(56,193,'StateBalancesController','overview','error','server response status code isn\\'t 200','403','2020-11-24 12:44:31'),
(57,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2020-12-31 19:17:52'),
(58,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2020-12-31 19:18:04'),
(59,44,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-12 20:58:45'),
(60,44,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-12 21:02:02'),
(61,44,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-12 21:02:04'),
(62,44,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-12 21:02:19'),
(63,20,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-13 22:32:59'),
(64,20,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-14 23:54:19'),
(65,161,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-27 10:29:09'),
(66,161,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-27 10:29:11'),
(67,161,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-01-27 10:29:23'),
(68,20,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-02-03 00:27:36'),
(69,685,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-02-06 11:48:09'),
(70,685,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-02-06 11:53:14'),
(71,685,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-02-06 11:53:20'),
(72,502,'TransactionCreations','createMulti','error','json exception','System exception: cannot unlock mutex','2021-03-24 21:22:14'),
(73,502,'TransactionCreations','createMulti','error','json exception','System exception: cannot unlock mutex','2021-03-24 21:23:38'),
(74,259,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-02 15:07:07'),
(75,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 20:31:50'),
(76,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 20:31:57'),
(77,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 20:32:23'),
(78,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 20:32:57'),
(79,199,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 21:43:13'),
(80,199,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 21:43:47'),
(81,199,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 21:44:45'),
(82,272,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 22:57:10'),
(83,272,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-14 22:57:18'),
(84,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:50:53'),
(85,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:51:27'),
(86,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:51:32'),
(87,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:51:54'),
(88,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:52:29'),
(89,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:53:33'),
(90,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-21 12:53:37'),
(91,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-23 12:27:57'),
(92,1162,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-23 12:28:02'),
(93,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 18:59:08'),
(94,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 18:59:16'),
(95,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 18:59:27'),
(96,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 19:00:51'),
(97,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 19:01:18'),
(98,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 19:04:22'),
(99,900,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 19:04:39'),
(100,1087,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-27 19:43:18'),
(101,240,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-28 22:00:05'),
(102,240,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-28 22:00:35'),
(103,240,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-28 22:00:37'),
(104,240,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-28 22:00:44'),
(105,90,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-29 14:05:59'),
(106,90,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-29 14:06:07'),
(107,90,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-29 14:06:15'),
(108,90,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-04-29 14:07:13'),
(109,79,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-01 10:18:03'),
(110,20,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-08 00:25:20'),
(111,84,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-08 22:34:27'),
(112,84,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-08 22:35:47'),
(113,776,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-12 10:24:55'),
(114,776,'TransactionSendCoins','create','success','(Leere Message)','(Leere Details)','2021-05-13 13:47:07'),
(115,1339,'StateBalancesController','overview','success','(Leere Message)','(Leere Details)','2021-07-13 08:54:30'),
(116,1339,'StateBalancesController','overview','success','(Leere Message)','(Leere Details)','2021-07-13 08:54:30'),
(117,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:46:40'),
(118,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:05'),
(119,943,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:05'),
(120,751,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:20'),
(121,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:21'),
(122,943,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:21'),
(123,751,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:22'),
(124,751,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:22'),
(125,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:36'),
(126,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:43'),
(127,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:43'),
(128,11,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:43'),
(129,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:46'),
(130,943,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:47:46'),
(131,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:48:47'),
(132,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:48:52'),
(133,11,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:48:52'),
(134,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:49:24'),
(135,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:49:26'),
(136,943,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:49:26'),
(137,751,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:49:58'),
(138,751,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:50:00'),
(139,751,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:50:00'),
(140,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:50:57'),
(141,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:51:01'),
(142,943,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:51:02'),
(143,11,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-08-12 14:51:10'),
(144,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:51:10'),
(145,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:52:39'),
(146,943,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:52:44'),
(147,82,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:54:25'),
(148,284,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-08-12 14:54:35'),
(149,1439,'StateBalancesController','overview','success','(Leere Message)','(Leere Details)','2021-09-16 15:44:16'),
(150,1439,'StateBalancesController','overview','success','(Leere Message)','(Leere Details)','2021-09-16 15:44:27'),
(151,11,'StateBalancesController','overview','error','server response status code isn\\'t 200','500','2021-09-22 11:03:10'),
(152,11,'StateBalancesController','ajaxGdtOverview','error','server response status code isn\\'t 200','500','2021-09-22 11:16:33'),
(153,82,'StateBalancesController','overview','error','server response status code isn\\'t 200','403','2021-11-23 16:03:57'),
(154,82,'StateBalancesController','overview','error','server response status code isn\\'t 200','403','2021-11-23 16:04:10');
`)
await queryFn(`
CREATE TABLE \`blockchain_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
\`text\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
\`symbol\` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`blockchain_types\` VALUES
(1,'mysql','use mysql db as blockchain, work only with single community-server',NULL),
(2,'hedera','use hedera for transactions','HBAR');
`)
await queryFn(`
CREATE TABLE \`community_profiles\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`state_user_id\` int(10) unsigned NOT NULL,
\`profile_img\` longblob DEFAULT NULL,
\`profile_desc\` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (\`id\`),
KEY \`state_user_id\` (\`state_user_id\`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: The data was removed due to large binary images in the database. If this data is needed please get it from the backup file.
await queryFn(`
CREATE TABLE \`login_email_opt_in_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(255) NOT NULL,
\`description\` varchar(255) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`login_email_opt_in_types\` VALUES
(1,'register','Email Verification Code for register from new User.'),
(2,'resetPassword','Email Verification Code for reset Password (only if passphrase is known)');
`)
await queryFn(`
CREATE TABLE \`login_groups\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`alias\` varchar(190) NOT NULL,
\`name\` varchar(255) NOT NULL,
\`url\` varchar(255) NOT NULL,
\`host\` varchar(255) DEFAULT '/',
\`home\` varchar(255) DEFAULT '/',
\`description\` text DEFAULT NULL,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`alias\` (\`alias\`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`login_groups\` VALUES
(1,'gdd1','gdd1','gdd1.gradido.com','','/','gdd1 group');
`)
await queryFn(`
CREATE TABLE \`login_roles\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(255) NOT NULL,
\`description\` varchar(255) NOT NULL,
\`flags\` bigint(20) NOT NULL DEFAULT 0,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`login_roles\` VALUES
(1,'admin','darf einfach alles',0);
`)
await queryFn(`
CREATE TABLE \`login_user_roles\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int(11) NOT NULL,
\`role_id\` int(11) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
`)
// NOTE: This data is not used - therefore we remove it.
// This data is aligned to the `server_users` table except the entry 4,
// this one is missing in the other table
// and after checking with administration, we figured out that
// this is a data fragement no longer needed.
await queryFn(`
INSERT INTO \`login_user_roles\` VALUES
(1,28,1),
(2,37,1),
(3,50,1),
(4,44,1),
(5,872,1);
`)
await queryFn(`
CREATE TABLE \`operators\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`username\` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
\`user_pubkey\` binary(32) NOT NULL,
\`data_base64\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
\`modified\` datetime NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: This data seems not to have any use
await queryFn(`
INSERT INTO \`operators\` VALUES
(5,'einhornimmond',0x78DCFAA8341B3A39B3C5502B4D9ACDBC4B181A10CC0D94187498E0A0C74288E0,'i99a5/wWGmQN4AF8ilUXhHJVV/3At82f6CNNh3ewdVyTTAqugcdeG53DEMNUxCTFwk7KIg==','2019-09-17 13:08:22');
`)
await queryFn(`
CREATE TABLE \`operator_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL,
\`text\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`operator_types\` VALUES
(1,'hedera','Hedera Keys for sign and pay hedera transactions'),
(2,'gradido-user','default gradido user keys'),
(3,'gradido-group','default gradido group root keys, other address are derived');
`)
await queryFn(`
CREATE TABLE \`state_errors\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`state_user_id\` int(10) unsigned NOT NULL,
\`transaction_type_id\` int(10) unsigned NOT NULL,
\`created\` datetime NOT NULL,
\`message_json\` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=161 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: This data is no longer generated
await queryFn(`
INSERT INTO \`state_errors\` VALUES
(9,11,1,'2020-08-07 10:40:03','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(17,528,2,'2021-02-10 08:04:32','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"sender 0 hasn\\'t enough GDD"}]}'),
(115,82,1,'2021-09-01 11:14:25','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(119,82,1,'2021-09-06 18:55:55','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(138,502,1,'2021-10-31 00:00:32','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(139,502,1,'2021-11-02 19:50:36','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(140,502,1,'2021-11-07 13:32:11','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(141,502,1,'2021-11-16 22:04:59','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(142,502,1,'2021-11-21 23:47:14','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(143,502,1,'2021-11-22 00:14:40','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(144,502,1,'2021-11-25 21:40:15','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(145,502,1,'2021-11-25 22:45:06','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(146,502,1,'2021-11-29 12:52:12','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(147,502,1,'2021-12-04 01:56:10','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(148,502,1,'2021-12-06 13:12:08','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(149,502,1,'2021-12-06 13:18:20','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(150,502,1,'2021-12-16 21:06:34','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(151,502,1,'2021-12-20 23:11:44','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(152,502,1,'2022-01-04 13:19:31','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(153,502,1,'2022-01-04 13:54:33','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(154,502,1,'2022-01-04 13:55:32','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(155,502,1,'2022-01-04 14:02:35','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(156,502,1,'2022-01-04 14:50:49','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(157,502,1,'2022-01-04 14:51:41','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(158,502,1,'2022-01-19 00:32:46','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(159,502,1,'2022-01-19 00:52:42','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}'),
(160,502,1,'2022-01-25 08:31:57','{"state":"error","msg":"error validate transaction","details":[{"TransactionCreation::validate":"Creation more than 1.000 GDD per Month for in target_date not allowed"}]}');
`)
await queryFn(`
CREATE TABLE \`transaction_types\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`name\` varchar(90) COLLATE utf8mb4_unicode_ci NOT NULL,
\`text\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
// NOTE: Static data might be needed as enum definitions
await queryFn(`
INSERT INTO \`transaction_types\` VALUES
(1,'creation','create new gradidos for member and also for group (in development)'),
(2,'transfer','send gradidos from one member to another, also cross group transfer'),
(3,'group create','create a new group, trigger creation of new hedera topic and new blockchain on node server'),
(4,'group add member','add user to a group or move if he was already in a group'),
(5,'group remove member','remove user from group, maybe he was moved elsewhere'),
(6,'hedera topic create','create new topic on hedera'),
(7,'hedera topic send message','send consensus message over hedera topic'),
(8,'hedera account create','create new account on hedera for holding some founds with unencrypted keys'),
(9,'decay start','signalize the starting point for decay calculation, allowed only once per chain');
`)
}

View File

@ -0,0 +1,35 @@
/* MIGRATION TO PROPERLY STORE PENDING CREATIONS
*
* There were two tables for the pending tasks,
* since the login_server used some crypto to store its
* tasks there. It was easier to create a new table.
* This migration drops the old unused table and renames
* the new table to properly describe what it does
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// drop duplicate table, it was unused
await queryFn('DROP TABLE `login_pending_tasks`;')
// rename the new pending creations table to a proper table name
await queryFn('RENAME TABLE `login_pending_tasks_admin` TO `admin_pending_creations`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn('RENAME TABLE `admin_pending_creations` TO `login_pending_tasks_admin`;')
await queryFn(`
CREATE TABLE \`login_pending_tasks\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int(10) unsigned DEFAULT 0,
\`request\` varbinary(2048) NOT NULL,
\`created\` datetime NOT NULL,
\`finished\` datetime DEFAULT '2000-01-01 00:00:00',
\`result_json\` text DEFAULT NULL,
\`param_json\` text DEFAULT NULL,
\`task_type_id\` int(10) unsigned NOT NULL,
\`child_pending_task_id\` int(10) unsigned DEFAULT 0,
\`parent_pending_task_id\` int(10) unsigned DEFAULT 0,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=795 DEFAULT CHARSET=utf8mb4
`)
}

View File

@ -0,0 +1,60 @@
/* MIGRATION TO CLEANUP TRANSACTIONS TABLE
*
* This migration cleans up the transactions table and
* combines its data with transaction_signatures.
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// Drop column `state_group_id` since it only contains "0" as value, no variation.
// Furthermore it was not present in our model itself (meaning that newly created )
await queryFn('ALTER TABLE `transactions` DROP COLUMN `state_group_id`;')
// Drop column `blockchain_type_id` since it only contains "1" as value, no variation.
await queryFn('ALTER TABLE `transactions` DROP COLUMN `blockchain_type_id`;')
// Create `signature` column - for data from `transaction_signatures` table.
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `signature` binary(64) DEFAULT NULL AFTER `received`;',
)
// Create `pubkey` column - for data from `transaction_signatures` table.
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `pubkey` binary(32) DEFAULT NULL AFTER `signature`;',
)
// Transfer data from `transaction_signatures` table to `transactions` table
await queryFn(`
UPDATE transactions
INNER JOIN transaction_signatures ON transactions.id = transaction_signatures.transaction_id
SET transactions.signature = transaction_signatures.signature, transactions.pubkey = transaction_signatures.pubkey;
`)
// Drop `transaction_signatures` table
await queryFn('DROP TABLE `transaction_signatures`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE \`transaction_signatures\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_id\` int(10) unsigned NOT NULL,
\`signature\` binary(64) NOT NULL,
\`pubkey\` binary(32) NOT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`)
await queryFn(`
INSERT INTO transaction_signatures (transaction_id, signature, pubkey)
(SELECT id as transaction_id, signature, pubkey FROM transactions WHERE signature IS NOT NULL and pubkey IS NOT NULL);
`)
await queryFn('ALTER TABLE `transactions` DROP COLUMN `pubkey`;')
await queryFn('ALTER TABLE `transactions` DROP COLUMN `signature`;')
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `blockchain_type_id` bigint(20) unsigned NOT NULL DEFAULT 1 AFTER `received` ;',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `state_group_id` int(10) unsigned DEFAULT NULL AFTER `id`;',
)
// We have to set the correct values previously in the table , since its not the same as the column's default
await queryFn('UPDATE `transactions` SET `state_group_id` = 0;')
}

View File

@ -1,16 +0,0 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { LoginUserRoles } from '../../entity/LoginUserRoles'
import { LoginUserRolesContext } from '../interface/UserContext'
define(LoginUserRoles, (faker: typeof Faker, context?: LoginUserRolesContext) => {
if (!context) context = {}
if (!context.userId) throw new Error('LoginUserRoles: No userId present!')
if (!context.roleId) throw new Error('LoginUserRoles: No roleId present!')
const userRoles = new LoginUserRoles()
userRoles.userId = context.userId
userRoles.roleId = context.roleId
return userRoles
})

View File

@ -1,18 +0,0 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { TransactionSignature } from '../../entity/TransactionSignature'
import { TransactionSignatureContext } from '../interface/TransactionContext'
import { randomBytes } from 'crypto'
define(TransactionSignature, (faker: typeof Faker, context?: TransactionSignatureContext) => {
if (!context || !context.transaction) {
throw new Error('TransactionSignature: No transaction present!')
}
const transactionSignature = new TransactionSignature()
transactionSignature.signature = context.signature ? context.signature : randomBytes(64)
transactionSignature.pubkey = context.pubkey ? context.pubkey : randomBytes(32)
transactionSignature.transaction = context.transaction
return transactionSignature
})

View File

@ -12,7 +12,8 @@ define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
transaction.txHash = context.txHash ? context.txHash : randomBytes(48)
transaction.memo = context.memo || context.memo === '' ? context.memo : faker.lorem.sentence()
transaction.received = context.received ? context.received : new Date()
transaction.blockchainTypeId = context.blockchainTypeId ? context.blockchainTypeId : 1
transaction.signature = context.signature ? context.signature : randomBytes(64)
transaction.pubkey = context.signaturePubkey ? context.signaturePubkey : randomBytes(32)
if (context.transactionSendCoin) transaction.transactionSendCoin = context.transactionSendCoin
if (context.transactionCreation) transaction.transactionCreation = context.transactionCreation

View File

@ -9,6 +9,8 @@ export interface TransactionContext {
memo?: string
received?: Date
blockchainTypeId?: number
signature?: Buffer
signaturePubkey?: Buffer
transactionSendCoin?: TransactionSendCoin
transactionCreation?: TransactionCreation
}
@ -43,10 +45,6 @@ export interface UserTransactionContext {
transactionTypeId?: number
balance?: number
balanceDate?: Date
}
export interface TransactionSignatureContext {
signature?: Buffer
pubkey?: Buffer
transaction?: Transaction
}

View File

@ -10,17 +10,14 @@ import {
TransactionContext,
TransactionCreationContext,
UserTransactionContext,
TransactionSignatureContext,
} from '../../interface/TransactionContext'
import { UserInterface } from '../../interface/UserInterface'
import { User } from '../../../entity/User'
import { LoginUser } from '../../../entity/LoginUser'
import { LoginUserBackup } from '../../../entity/LoginUserBackup'
import { ServerUser } from '../../../entity/ServerUser'
import { LoginUserRoles } from '../../../entity/LoginUserRoles'
import { Balance } from '../../../entity/Balance'
import { Transaction } from '../../../entity/Transaction'
import { TransactionSignature } from '../../../entity/TransactionSignature'
import { UserTransaction } from '../../../entity/UserTransaction'
import { TransactionCreation } from '../../../entity/TransactionCreation'
import { Factory } from 'typeorm-seeding'
@ -33,10 +30,6 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
if (userData.isAdmin) {
await factory(ServerUser)(createServerUserContext(userData)).create()
// This is crazy: we just need the relation to roleId but no role at all
// It works with LoginRoles empty!!
await factory(LoginUserRoles)(createLoginUserRolesContext(loginUser)).create()
}
if (userData.addBalance) {
@ -51,9 +44,6 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
await factory(UserTransaction)(
createUserTransactionContext(userData, user, transaction),
).create()
await factory(TransactionSignature)(
createTransactionSignatureContext(userData, transaction),
).create()
}
}
@ -166,16 +156,7 @@ const createUserTransactionContext = (
transactionTypeId: transaction.transactionTypeId,
balance: context.amount,
balanceDate: context.recordDate,
}
}
const createTransactionSignatureContext = (
context: UserInterface,
transaction: Transaction,
): TransactionSignatureContext => {
return {
signature: context.signature,
pubkey: context.signaturePubkey,
transaction,
}
}