mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-17 18:32:35 +00:00
Implemented premium license check per group for the web-frontend
This commit is contained in:
parent
40014ab4ea
commit
8935b66c23
36 changed files with 344 additions and 55 deletions
premium
backend/src/baserow_premium
web-frontend
modules/baserow_premium
test/unit/premium
web-frontend
modules/database
test/unit/database/components
|
@ -1,6 +1,6 @@
|
|||
from baserow.api.user.registries import UserDataType
|
||||
|
||||
from baserow_premium.license.handler import has_active_premium_license
|
||||
from baserow_premium.license.handler import has_active_premium_license_for
|
||||
|
||||
|
||||
class PremiumUserDataType(UserDataType):
|
||||
|
@ -12,4 +12,6 @@ class PremiumUserDataType(UserDataType):
|
|||
user has a valid license for the premioum version.
|
||||
"""
|
||||
|
||||
return {"valid_license": has_active_premium_license(user)}
|
||||
return {
|
||||
"valid_license": has_active_premium_license_for(user),
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import binascii
|
|||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
from typing import Union, List
|
||||
from typing import Union, List, Dict, Any
|
||||
from os.path import dirname, join
|
||||
from dateutil import parser
|
||||
|
||||
|
@ -75,7 +75,7 @@ def has_active_premium_license(user: DjangoUser) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def check_active_premium_license(user):
|
||||
def check_active_premium_license(user: DjangoUser):
|
||||
"""
|
||||
Raises the `NoPremiumLicenseError` if the user does not have an active premium
|
||||
license.
|
||||
|
@ -85,6 +85,35 @@ def check_active_premium_license(user):
|
|||
raise NoPremiumLicenseError()
|
||||
|
||||
|
||||
def has_active_premium_license_for(
|
||||
user: DjangoUser,
|
||||
) -> Union[bool, List[Dict[str, Any]]]:
|
||||
"""
|
||||
Check for which objects the user has an active license. If `True` is returned it
|
||||
means that the user has premium access to everything. If an object is returned,
|
||||
it means that the user only has access to the specific objects. For now,
|
||||
it's only possible to grant access to specific groups.
|
||||
|
||||
Example complex return value:
|
||||
|
||||
[
|
||||
{
|
||||
"type": "group",
|
||||
"id": 1,
|
||||
},
|
||||
{
|
||||
"type": "group",
|
||||
"id": 2,
|
||||
}
|
||||
]
|
||||
|
||||
:param user: The user for whom must be checked if it has an active license.
|
||||
:return: To which groups the user has an active premium license for.
|
||||
"""
|
||||
|
||||
return has_active_premium_license(user)
|
||||
|
||||
|
||||
def get_public_key():
|
||||
"""
|
||||
Returns the public key instance that can be used to verify licenses. A different
|
||||
|
|
|
@ -77,6 +77,10 @@ export default {
|
|||
RowComment,
|
||||
},
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
required: true,
|
||||
type: Object,
|
||||
|
@ -97,7 +101,10 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
validPremiumLicense() {
|
||||
return PremiumPlugin.hasValidPremiumLicense(this.additionalUserData)
|
||||
return PremiumPlugin.hasValidPremiumLicense(
|
||||
this.additionalUserData,
|
||||
this.database.group.id
|
||||
)
|
||||
},
|
||||
...mapGetters({
|
||||
comments: 'row_comments/getSortedRowComments',
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
></RowCreateModal>
|
||||
<RowEditModal
|
||||
ref="rowEditModal"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:primary="primary"
|
||||
:primary-is-sortable="true"
|
||||
|
|
|
@ -6,8 +6,23 @@ export class PremiumPlugin extends BaserowPlugin {
|
|||
return 'premium'
|
||||
}
|
||||
|
||||
static hasValidPremiumLicense(additionalUserData) {
|
||||
return additionalUserData?.premium?.valid_license
|
||||
/**
|
||||
* @param additionalUserData The additional user data that contains to which group
|
||||
* the user has a premium license for.
|
||||
* @param forGroup If provided, the user must explicitly have an active license
|
||||
* for that group or for all groups.
|
||||
* @return boolean
|
||||
*/
|
||||
static hasValidPremiumLicense(additionalUserData, forGroupId = undefined) {
|
||||
const validLicense = additionalUserData?.premium?.valid_license
|
||||
const groups = Array.isArray(validLicense)
|
||||
? validLicense.filter((o) => o.type === 'group').map((o) => o.id)
|
||||
: []
|
||||
return (
|
||||
validLicense === true ||
|
||||
(Array.isArray(validLicense) && !forGroupId) ||
|
||||
(Array.isArray(validLicense) && groups.includes(forGroupId))
|
||||
)
|
||||
}
|
||||
|
||||
getSidebarTopComponent() {
|
||||
|
|
|
@ -9,9 +9,10 @@ class PremiumTableExporterType extends TableExporterType {
|
|||
return this.app.i18n.t('premium.deactivated')
|
||||
}
|
||||
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
return !PremiumPlugin.hasValidPremiumLicense(
|
||||
this.app.store.getters['auth/getAdditionalUserData']
|
||||
this.app.store.getters['auth/getAdditionalUserData'],
|
||||
groupId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ export class LeftBorderColorViewDecoratorType extends ViewDecoratorType {
|
|||
return i18n.t('viewDecoratorType.onlyForPremium')
|
||||
}
|
||||
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
const { store } = this.app
|
||||
|
||||
const additionalUserData = store.getters['auth/getAdditionalUserData']
|
||||
|
||||
if (PremiumPlugin.hasValidPremiumLicense(additionalUserData)) {
|
||||
if (PremiumPlugin.hasValidPremiumLicense(additionalUserData, groupId)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -141,12 +141,12 @@ export class BackgroundColorViewDecoratorType extends ViewDecoratorType {
|
|||
return i18n.t('viewDecoratorType.onlyForPremium')
|
||||
}
|
||||
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
const { store } = this.app
|
||||
|
||||
const additionalUserData = store.getters['auth/getAdditionalUserData']
|
||||
|
||||
if (PremiumPlugin.hasValidPremiumLicense(additionalUserData)) {
|
||||
if (PremiumPlugin.hasValidPremiumLicense(additionalUserData, groupId)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -12,9 +12,10 @@ class PremiumViewType extends ViewType {
|
|||
return this.app.i18n.t('premium.deactivated')
|
||||
}
|
||||
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
return !PremiumPlugin.hasValidPremiumLicense(
|
||||
this.app.store.getters['auth/getAdditionalUserData']
|
||||
this.app.store.getters['auth/getAdditionalUserData'],
|
||||
groupId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
120
premium/web-frontend/test/unit/premium/plugins.spec.js
Normal file
120
premium/web-frontend/test/unit/premium/plugins.spec.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
import { PremiumPlugin } from '@baserow_premium/plugins'
|
||||
|
||||
describe('Test premium Baserow plugin', () => {
|
||||
test('Test hasValidPremiumLicense method', () => {
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense({ premium: { valid_license: true } })
|
||||
).toBe(true)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense({
|
||||
premium: { valid_license: false },
|
||||
})
|
||||
).toBe(false)
|
||||
expect(PremiumPlugin.hasValidPremiumLicense({ premium: {} })).toBe(false)
|
||||
expect(PremiumPlugin.hasValidPremiumLicense({})).toBe(false)
|
||||
|
||||
// If the `valid_license` is `true`, the user has premium access features
|
||||
// enabled for all groups.
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{ premium: { valid_license: true } },
|
||||
1
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{ premium: { valid_license: false } },
|
||||
1
|
||||
)
|
||||
).toBe(false)
|
||||
|
||||
// If the user only has premium access to certain groups, we expect false
|
||||
// for other groups.
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{ premium: { valid_license: [] } },
|
||||
1
|
||||
)
|
||||
).toBe(false)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{ premium: { valid_license: [{ type: 'group', id: 2 }] } },
|
||||
1
|
||||
)
|
||||
).toBe(false)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{
|
||||
premium: {
|
||||
valid_license: [
|
||||
{ type: 'group', id: 2 },
|
||||
{ type: 'group', id: 3 },
|
||||
],
|
||||
},
|
||||
},
|
||||
1
|
||||
)
|
||||
).toBe(false)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{
|
||||
premium: {
|
||||
valid_license: [
|
||||
{ type: 'group', id: 2 },
|
||||
{ type: 'group', id: 3 },
|
||||
],
|
||||
},
|
||||
},
|
||||
4
|
||||
)
|
||||
).toBe(false)
|
||||
|
||||
// If the user only has premium access to certain groups, he will have
|
||||
// access to the premium features that are not related to a group.
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense({
|
||||
premium: { valid_license: [{ type: 'group', id: 1 }] },
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense({
|
||||
premium: { valid_license: [{ type: 'group', id: 2 }] },
|
||||
})
|
||||
).toBe(true)
|
||||
|
||||
// If the user only has premium access to certain groups, we expect the
|
||||
// matches group to be true.
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{ premium: { valid_license: [{ type: 'group', id: 1 }] } },
|
||||
1
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{
|
||||
premium: {
|
||||
valid_license: [
|
||||
{ type: 'group', id: 1 },
|
||||
{ type: 'group', id: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
1
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
PremiumPlugin.hasValidPremiumLicense(
|
||||
{
|
||||
premium: {
|
||||
valid_license: [
|
||||
{ type: 'group', id: 1 },
|
||||
{ type: 'group', id: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
2
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
|
@ -19,6 +19,7 @@
|
|||
v-model="values.exporter_type"
|
||||
:exporter-types="exporterTypes"
|
||||
:loading="loading"
|
||||
:database="database"
|
||||
></ExporterTypeChoices>
|
||||
<div v-if="$v.values.exporter_type.$error" class="error">
|
||||
{{ $t('exportTableForm.typeError') }}
|
||||
|
@ -51,6 +52,10 @@ export default {
|
|||
},
|
||||
mixins: [form],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<ExportTableForm
|
||||
ref="form"
|
||||
v-slot="{ filename }"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:view="view"
|
||||
:views="views"
|
||||
|
@ -39,6 +40,10 @@ export default {
|
|||
components: { ExportTableForm, ExportTableLoadingBar },
|
||||
mixins: [modal, error],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
export default {
|
||||
name: 'ExporterTypeChoice',
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
exporterType: {
|
||||
required: true,
|
||||
type: Object,
|
||||
|
@ -41,7 +45,7 @@ export default {
|
|||
deactivated() {
|
||||
return this.$registry
|
||||
.get('exporter', this.exporterType.type)
|
||||
.isDeactivated()
|
||||
.isDeactivated(this.database.group.id)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
:exporter-type="exporterType"
|
||||
:active="value !== null && value === exporterType.type"
|
||||
:disabled="loading"
|
||||
:database="database"
|
||||
@selected="switchToExporterType(exporterType.type)"
|
||||
>
|
||||
</ExporterTypeChoice>
|
||||
|
@ -28,6 +29,10 @@ export default {
|
|||
name: 'ExporterTypeChoices',
|
||||
components: { ExporterTypeChoice },
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
exporterTypes: {
|
||||
required: true,
|
||||
type: Array,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<RowEditModal
|
||||
ref="modal"
|
||||
:read-only="true"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:rows="[]"
|
||||
:visible-fields="fields"
|
||||
|
@ -39,6 +40,26 @@ export default {
|
|||
primary: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
database() {
|
||||
const databaseType = DatabaseApplicationType.getType()
|
||||
for (const application of this.$store.getters['application/getAll']) {
|
||||
if (application.type !== databaseType) {
|
||||
continue
|
||||
}
|
||||
|
||||
const foundTable = application.tables.find(
|
||||
({ id }) => id === this.tableId
|
||||
)
|
||||
|
||||
if (foundTable) {
|
||||
return application
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async fetchTableAndFields() {
|
||||
// Find the table in the applications to prevent a request to the backend and to
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
:row="row"
|
||||
:read-only="readOnly"
|
||||
:table="table"
|
||||
:database="database"
|
||||
></component>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -89,6 +90,10 @@ export default {
|
|||
},
|
||||
mixins: [modal],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -51,7 +51,11 @@
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ExportTableModal ref="exportTableModal" :table="table" />
|
||||
<ExportTableModal
|
||||
ref="exportTableModal"
|
||||
:database="database"
|
||||
:table="table"
|
||||
/>
|
||||
<WebhookModal ref="webhookModal" :table="table" />
|
||||
</Context>
|
||||
</li>
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<ViewsContext
|
||||
v-if="views !== null"
|
||||
ref="viewsContext"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:views="views"
|
||||
:read-only="readOnly"
|
||||
|
@ -65,6 +66,7 @@
|
|||
</a>
|
||||
<ViewContext
|
||||
ref="viewContext"
|
||||
:database="database"
|
||||
:view="view"
|
||||
:table="table"
|
||||
@enable-rename="$refs.rename.edit()"
|
||||
|
@ -108,6 +110,7 @@
|
|||
class="header__filter-item"
|
||||
>
|
||||
<ViewDecoratorMenu
|
||||
:database="database"
|
||||
:view="view"
|
||||
:table="table"
|
||||
:fields="fields"
|
||||
|
|
|
@ -31,6 +31,10 @@ export default {
|
|||
CreateViewModal,
|
||||
},
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -45,7 +49,7 @@ export default {
|
|||
return this.viewType.getDeactivatedText()
|
||||
},
|
||||
deactivated() {
|
||||
return this.viewType.isDeactivated()
|
||||
return this.viewType.isDeactivated(this.database.group.id)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<template>
|
||||
<Context ref="context">
|
||||
<ViewDecoratorList :view="view" @select="$emit('select', $event)" />
|
||||
<ViewDecoratorList
|
||||
:database="database"
|
||||
:view="view"
|
||||
@select="$emit('select', $event)"
|
||||
/>
|
||||
</Context>
|
||||
</template>
|
||||
|
||||
|
@ -15,6 +19,10 @@ export default {
|
|||
},
|
||||
mixins: [context],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
view: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -26,7 +26,12 @@
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ExportTableModal ref="exportViewModal" :table="table" :view="view" />
|
||||
<ExportTableModal
|
||||
ref="exportViewModal"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:view="view"
|
||||
/>
|
||||
<WebhookModal ref="webhookModal" :table="table" />
|
||||
</Context>
|
||||
</template>
|
||||
|
@ -44,6 +49,10 @@ export default {
|
|||
components: { ExportTableModal, WebhookModal },
|
||||
mixins: [context, error],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
view: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<Context>
|
||||
<ViewDecoratorList
|
||||
v-if="activeDecorations.length === 0"
|
||||
:database="database"
|
||||
:view="view"
|
||||
@select="addDecoration($event)"
|
||||
/>
|
||||
|
@ -92,6 +93,7 @@
|
|||
</a>
|
||||
<SelectViewDecoratorContext
|
||||
ref="selectDecoratorContext"
|
||||
:database="database"
|
||||
:view="view"
|
||||
@select="
|
||||
;[$refs.selectDecoratorContext.hide(), addDecoration($event)]
|
||||
|
@ -123,6 +125,10 @@ export default {
|
|||
},
|
||||
mixins: [context, viewDecoration],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
primary: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -20,6 +20,10 @@ export default {
|
|||
name: 'ViewDecoratorList',
|
||||
components: { ViewDecoratorItem },
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
view: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -35,17 +39,12 @@ export default {
|
|||
methods: {
|
||||
isDisabled(decoratorType) {
|
||||
return (
|
||||
decoratorType.isDeactivated({
|
||||
view: this.view,
|
||||
}) || !decoratorType.canAdd({ view: this.view })[0]
|
||||
decoratorType.isDeactivated(this.database.group.id) ||
|
||||
!decoratorType.canAdd({ view: this.view })[0]
|
||||
)
|
||||
},
|
||||
getTooltip(decoratorType) {
|
||||
if (
|
||||
decoratorType.isDeactivated({
|
||||
view: this.view,
|
||||
})
|
||||
) {
|
||||
if (decoratorType.isDeactivated(this.database.group.id)) {
|
||||
return decoratorType.getDeactivatedText()
|
||||
}
|
||||
const [canAdd, disabledReason] = decoratorType.canAdd({ view: this.view })
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
</a>
|
||||
<ViewDecoratorContext
|
||||
ref="context"
|
||||
:database="database"
|
||||
:view="view"
|
||||
:table="table"
|
||||
:fields="fields"
|
||||
|
@ -34,6 +35,10 @@ export default {
|
|||
name: 'ViewDecoratorMenu',
|
||||
components: { ViewDecoratorContext },
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
primary: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -60,7 +65,7 @@ export default {
|
|||
return this.view.decorations.filter(({ type }) => {
|
||||
return !this.$registry
|
||||
.get('viewDecorator', type)
|
||||
.isDeactivated({ view: this.view })
|
||||
.isDeactivated(this.database.group.id)
|
||||
}).length
|
||||
},
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
update: order,
|
||||
marginTop: -1.5,
|
||||
}"
|
||||
:database="database"
|
||||
:view="view"
|
||||
:table="table"
|
||||
:read-only="readOnly"
|
||||
|
@ -42,6 +43,7 @@
|
|||
<CreateViewLink
|
||||
v-for="(viewType, type) in viewTypes"
|
||||
:key="type"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:view-type="viewType"
|
||||
@created="selectedView"
|
||||
|
@ -69,6 +71,10 @@ export default {
|
|||
},
|
||||
mixins: [context, dropdownHelpers],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
</a>
|
||||
<ViewContext
|
||||
ref="context"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:view="view"
|
||||
@enable-rename="enableRename"
|
||||
|
@ -54,6 +55,10 @@ export default {
|
|||
components: { EditableViewName, ViewContext },
|
||||
mixins: [context],
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
view: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -79,7 +84,7 @@ export default {
|
|||
!this.readOnly &&
|
||||
this.$registry
|
||||
.get('view', this.view.type)
|
||||
.isDeactivated({ view: this.view })
|
||||
.isDeactivated(this.database.group.id)
|
||||
)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
></RowCreateModal>
|
||||
<RowEditModal
|
||||
ref="rowEditModal"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:primary="primary"
|
||||
:primary-is-sortable="true"
|
||||
|
|
|
@ -182,6 +182,7 @@
|
|||
</Context>
|
||||
<RowEditModal
|
||||
ref="rowEditModal"
|
||||
:database="database"
|
||||
:table="table"
|
||||
:primary="primary"
|
||||
:visible-fields="[primary].concat(visibleFields)"
|
||||
|
|
|
@ -75,7 +75,7 @@ export class TableExporterType extends Registerable {
|
|||
/**
|
||||
* Indicates if the exporter type is disabled.
|
||||
*/
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
*/
|
||||
export default {
|
||||
props: {
|
||||
database: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
view: {
|
||||
type: Object,
|
||||
required: false,
|
||||
|
@ -57,7 +62,7 @@ export default {
|
|||
})
|
||||
.filter(
|
||||
({ decoratorType }) =>
|
||||
!decoratorType.isDeactivated({ view: this.view })
|
||||
!decoratorType.isDeactivated(this.database.group.id)
|
||||
)
|
||||
},
|
||||
decorationsByPlace() {
|
||||
|
|
|
@ -97,7 +97,7 @@ export default {
|
|||
// filled with initial data so we're going to call the fetch function here.
|
||||
const type = app.$registry.get('view', view.type)
|
||||
|
||||
if (type.isDeactivated()) {
|
||||
if (type.isDeactivated(data.database.group.id)) {
|
||||
return error({ statusCode: 400, message: type.getDeactivatedText() })
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export class ViewDecoratorType extends Registerable {
|
|||
/**
|
||||
* Indicates if the decorator type is disabled.
|
||||
*/
|
||||
isDeactivated({ view }) {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ export class ViewType extends Registerable {
|
|||
/**
|
||||
* Indicates if the view type is disabled.
|
||||
*/
|
||||
isDeactivated() {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -16,19 +16,20 @@ describe('Preview exportTableModal', () => {
|
|||
|
||||
async function givenThereIsATableWithView() {
|
||||
const table = mockServer.createTable()
|
||||
const database = { tables: [] }
|
||||
const database = { tables: [], group: { id: 0 } }
|
||||
await testApp.store.dispatch('table/forceCreate', {
|
||||
database,
|
||||
data: table,
|
||||
})
|
||||
const view = mockServer.createGridView(database, table, {})
|
||||
return { table, view }
|
||||
return { database, table, view }
|
||||
}
|
||||
|
||||
test('Modal with no view', async () => {
|
||||
const { table } = await givenThereIsATableWithView()
|
||||
const { table, database } = await givenThereIsATableWithView()
|
||||
const wrapper = await testApp.mount(ExportTableModal, {
|
||||
propsData: {
|
||||
database,
|
||||
table,
|
||||
view: null,
|
||||
},
|
||||
|
@ -42,9 +43,10 @@ describe('Preview exportTableModal', () => {
|
|||
})
|
||||
|
||||
test('Modal with view', async () => {
|
||||
const { table, view } = await givenThereIsATableWithView()
|
||||
const { table, view, database } = await givenThereIsATableWithView()
|
||||
const wrapper = await testApp.mount(ExportTableModal, {
|
||||
propsData: {
|
||||
database,
|
||||
table,
|
||||
view,
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ export class FakeDecoratorType extends ViewDecoratorType {
|
|||
return 'first_cell'
|
||||
}
|
||||
|
||||
isDeactivated({ view }) {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -138,11 +138,11 @@ describe('GalleryView component with decoration', () => {
|
|||
})
|
||||
await store.dispatch('view/fetchAll', { id: 1 })
|
||||
|
||||
return { table, primary, fields, view }
|
||||
return { application, table, primary, fields, view }
|
||||
}
|
||||
|
||||
test('Default component with first_cell decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -162,6 +162,7 @@ describe('GalleryView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
@ -174,7 +175,7 @@ describe('GalleryView component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Default component with row wrapper decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -210,6 +211,7 @@ describe('GalleryView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
@ -222,7 +224,7 @@ describe('GalleryView component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Default component with unavailable decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -239,6 +241,7 @@ describe('GalleryView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
|
|
@ -12,7 +12,7 @@ export class FakeDecoratorType extends ViewDecoratorType {
|
|||
return 'first_cell'
|
||||
}
|
||||
|
||||
isDeactivated({ view }) {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -137,11 +137,11 @@ describe('GridView component with decoration', () => {
|
|||
primary,
|
||||
})
|
||||
await store.dispatch('view/fetchAll', { id: 1 })
|
||||
return { table, primary, fields, view }
|
||||
return { application, table, primary, fields, view }
|
||||
}
|
||||
|
||||
test('Default component with first_cell decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -161,6 +161,7 @@ describe('GridView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
@ -173,7 +174,7 @@ describe('GridView component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Default component with row wrapper decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -209,6 +210,7 @@ describe('GridView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
@ -221,7 +223,7 @@ describe('GridView component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Default component with unavailable decoration', async () => {
|
||||
const { table, primary, fields, view } = await populateStore([
|
||||
const { application, table, primary, fields, view } = await populateStore([
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
value_provider_type: 'fake_value_provider_type',
|
||||
|
@ -238,6 +240,7 @@ describe('GridView component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper1 = await mountComponent({
|
||||
database: application,
|
||||
table,
|
||||
view,
|
||||
primary,
|
||||
|
|
|
@ -24,7 +24,7 @@ export class FakeDecoratorType extends ViewDecoratorType {
|
|||
return true
|
||||
}
|
||||
|
||||
isDeactivated({ view }) {
|
||||
isDeactivated(groupId) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
primary,
|
||||
})
|
||||
await store.dispatch('view/fetchAll', { id: table.id })
|
||||
return { table, primary, fields, view }
|
||||
return { application, table, primary, fields, view }
|
||||
}
|
||||
|
||||
const mountComponent = async (props) => {
|
||||
|
@ -181,7 +181,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
}
|
||||
|
||||
test('Default component', async () => {
|
||||
const { table, primary, fields, view } = await populateStore()
|
||||
const { application, table, primary, fields, view } = await populateStore()
|
||||
|
||||
const fakeDecorator = new FakeDecoratorType({ app: testApp })
|
||||
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
|
||||
|
@ -190,6 +190,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper = await mountComponent({
|
||||
database: application,
|
||||
view,
|
||||
table,
|
||||
primary,
|
||||
|
@ -201,7 +202,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
})
|
||||
|
||||
test('View with decoration configured', async () => {
|
||||
const { table, primary, fields, view } = await populateStore({
|
||||
const { application, table, primary, fields, view } = await populateStore({
|
||||
decorations: [
|
||||
{
|
||||
type: 'fake_decorator',
|
||||
|
@ -225,6 +226,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper = await mountComponent({
|
||||
database: application,
|
||||
view,
|
||||
table,
|
||||
primary,
|
||||
|
@ -236,7 +238,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Should show unavailable decorator tooltip', async () => {
|
||||
const { table, primary, fields, view } = await populateStore()
|
||||
const { application, table, primary, fields, view } = await populateStore()
|
||||
|
||||
const fakeDecorator = new FakeDecoratorType({ app: testApp })
|
||||
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
|
||||
|
@ -247,6 +249,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper = await mountComponent({
|
||||
database: application,
|
||||
view,
|
||||
table,
|
||||
primary,
|
||||
|
@ -262,7 +265,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
})
|
||||
|
||||
test('Should show cant add decorator tooltip', async () => {
|
||||
const { table, primary, fields, view } = await populateStore()
|
||||
const { application, table, primary, fields, view } = await populateStore()
|
||||
|
||||
const fakeDecorator = new FakeDecoratorType({ app: testApp })
|
||||
const fakeValueProvider = new FakeValueProviderType({ app: testApp })
|
||||
|
@ -273,6 +276,7 @@ describe('GridViewRows component with decoration', () => {
|
|||
store.$registry.register('decoratorValueProvider', fakeValueProvider)
|
||||
|
||||
const wrapper = await mountComponent({
|
||||
database: application,
|
||||
view,
|
||||
table,
|
||||
primary,
|
||||
|
|
Loading…
Add table
Reference in a new issue