Skip to content

Commit

Permalink
feat: ✨ upsert settings in db
Browse files Browse the repository at this point in the history
  • Loading branch information
clairenollet authored and this-is-tobi committed Sep 24, 2024
1 parent 0d2b6f2 commit 9474fd9
Show file tree
Hide file tree
Showing 38 changed files with 554 additions and 471 deletions.
30 changes: 16 additions & 14 deletions apps/client/cypress/e2e/specs/admin/system-settings.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SystemSettings } from '@cpn-console/shared'
import { type SystemSettings, SystemSettingsSchema } from '@cpn-console/shared'
import { getModel } from '../../support/func.js'

describe('Administration system settings', () => {
Expand All @@ -7,7 +7,7 @@ describe('Administration system settings', () => {
beforeEach(() => {
cy.intercept('GET', 'api/v1/system/settings?key=maintenance').as('listMaintenanceSetting')
cy.intercept('GET', 'api/v1/system/settings').as('listSystemSettings')
cy.intercept('POST', 'api/v1/system/settings').as('upsertSystemSetting')
cy.intercept('POST', 'api/v1/system/settings').as('upsertSystemSettings')

cy.kcLogin('tcolin')
cy.visit('/admin/system-settings')
Expand All @@ -29,12 +29,13 @@ describe('Administration system settings', () => {
cy.getByDataTestid(`toggle-maintenance`)
.find('input')
.check({ force: true })
cy.wait('@upsertSystemSetting').its('response').then(($response) => {

cy.getByDataTestid('button-submit')
.click()

cy.wait('@upsertSystemSettings').its('response').then(($response) => {
expect($response?.statusCode).to.match(/^20\d$/)
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify({
key: 'maintenance',
value: 'on',
}))
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify(SystemSettingsSchema.parse({})))
})

cy.visit('/projects')
Expand All @@ -61,7 +62,7 @@ describe('Administration system settings', () => {
expect($response?.statusCode).to.match(/^20\d$/)
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify([{
key: 'maintenance',
value: 'on',
value: 'true',
}]))
})
cy.wait('@listRoles')
Expand All @@ -83,20 +84,21 @@ describe('Administration system settings', () => {
cy.getByDataTestid(`toggle-maintenance`)
.find('input')
.uncheck({ force: true })
cy.wait('@upsertSystemSetting').its('response').then(($response) => {

cy.getByDataTestid('button-submit')
.click()

cy.wait('@upsertSystemSettings').its('response').then(($response) => {
expect($response?.statusCode).to.match(/^20\d$/)
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify({
key: 'maintenance',
value: 'off',
}))
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify(SystemSettingsSchema.parse({})))
})

cy.visit('/projects')
cy.wait('@listMaintenanceSetting').its('response').then(($response) => {
expect($response?.statusCode).to.match(/^20\d$/)
expect(JSON.stringify($response?.body)).to.equal(JSON.stringify([{
key: 'maintenance',
value: 'on',
value: 'true',
}]))
})
cy.getByDataTestid('maintenance-notice')
Expand Down
17 changes: 7 additions & 10 deletions apps/client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import type { SystemSettings } from '@cpn-console/shared'
import { apiPrefix } from '@cpn-console/shared'
import { getKeycloak } from './utils/keycloak/keycloak.js'
import { useSnackbarStore } from './stores/snackbar.js'
Expand Down Expand Up @@ -39,26 +38,24 @@ watch(label, (label: string) => {
quickLinks.value[0].label = label
})
const systemSettings = ref<SystemSettings>()
const systemSettings = computed(() => systemStore.systemSettings)
const serviceStore = useServiceStore()
onBeforeMount(() => {
serviceStore.startHealthPolling()
serviceStore.checkServicesHealth()
systemStore.listSystemSettings()
systemSettings.value = systemStore.systemSettings
onBeforeMount(async () => {
await serviceStore.startHealthPolling()
await serviceStore.checkServicesHealth()
await systemStore.listSystemSettings()
})
</script>

<template>
<DsfrHeader
:service-title="systemSettings?.appName ?? 'Console Cloud π Native'"
:logo-text="systemSettings?.appSubTitle ?? ['Ministère', 'de l’intérieur', 'et des outre-mer']"
:logo-text="systemSettings?.appSubTitle.split(',') ?? ['Ministère', 'de l’intérieur', 'et des outre-mer']"
:quick-links="quickLinks"
/>
<DsfrNotice
v-if="systemStore.systemSettings?.maintenance === 'on'"
v-if="systemStore.systemSettings?.maintenance === 'true'"
title="Le mode Maintenance est actuellement activé"
data-testid="maintenance-notice"
/>
Expand Down
30 changes: 23 additions & 7 deletions apps/client/src/components/ConfigParam.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { DEFAULT, DISABLED, ENABLED } from '@cpn-console/shared'
const props = defineProps<{
options: {
value: Ref<string>
value: Ref<string | boolean>
description: string | undefined
name: string
name: string | undefined
disabled: boolean
kind: 'text' | 'switch'
kind: 'text' | 'switch' | 'realSwitch'
placeholder: string | undefined
label: string | undefined
}
}>()
const emit = defineEmits<{
update: [data: string]
update: [data: string | boolean]
}>()
const switchOptions = [
{
Expand All @@ -39,7 +40,7 @@ const switchOptionsDisabled = switchOptions.map(options => ({ ...options, disabl
const value = ref(props.options.value)
function set(data: string) {
function set(data: string | boolean) {
value.value = data
emit('update', data)
}
Expand All @@ -55,7 +56,8 @@ function set(data: string) {
<DsfrInput
v-if="props.options.kind === 'text' && !props.options.disabled"
:model-value="value"
:label-visible="false"
:label="props.options.label"
:label-visible="!!props.options.label"
:placeholder="props.options.placeholder"
data-testid="input"
@update:model-value="(event: string) => set(event)"
Expand All @@ -72,12 +74,25 @@ function set(data: string) {
:name="options.name"
:model-value="value"
:options="props.options.disabled ? switchOptionsDisabled : switchOptions"
:label-visible="false"
:label="props.options.label"
:label-visible="!!props.options.label"
inline
:small="false"
data-testid="switch"
@update:model-value="(event: string | number) => set(String(event))"
/>

<DsfrToggleSwitch
v-else-if="props.options.kind === 'realSwitch'"
:name="options.name"
:model-value="value"
:options="props.options.disabled ? switchOptionsDisabled : switchOptions"
:label="props.options.label"
inline
:small="false"
data-testid="realSwitch"
@update:model-value="(event: boolean) => set(event)"
/>
</div>
<div
v-if="props.options.description"
Expand All @@ -86,6 +101,7 @@ function set(data: string) {
{{ props.options.description }}
</div>
<hr
v-if="props.options.description"
class="col-span-2 p-1"
>
</template>
9 changes: 6 additions & 3 deletions apps/client/src/components/ServicesConfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ function refTheValues(services: ProjectService[]) {
const updated = ref<PluginsUpdateBody>({})
function update(data: { value: string, key: string, plugin: string }) {
function update(data: { value: string | boolean, key: string, plugin: string }) {
if (typeof data.value === 'boolean') return
if (!updated.value[data.plugin]) updated.value[data.plugin] = {}
updated.value[data.plugin][data.key] = data.value
}
Expand Down Expand Up @@ -175,11 +176,12 @@ function reload() {
kind: item.kind,
description: item.description,
name: item.title,
label: undefined,
// @ts-ignore Sisi il y a potentiellement un placeholder
placeholder: item.placeholder || '',
disabled: !item.permissions[permissionTarget].write,
}"
@update="(value: string) => update({ key: item.key, value, plugin: service.name })"
@update="(value: string | boolean) => update({ key: item.key, value, plugin: service.name })"
/>
<div
v-if="service.manifest.global?.length && props.displayGlobal"
Expand All @@ -195,11 +197,12 @@ function reload() {
kind: item.kind,
description: item.description,
name: item.title,
label: undefined,
// @ts-ignore si si il y a potentiellement un placeholder
placeholder: item.placeholder || '',
disabled: !item.permissions[permissionTarget].write,
}"
@update="(value: string) => update({ key: item.key, value, plugin: service.name })"
@update="(value: string | boolean) => update({ key: item.key, value, plugin: service.name })"
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ router.beforeEach(async (to, _from, next) => {
!validPath.includes(to.name)
&& userStore.isLoggedIn
) {
await systemStore.listSystemSettings('maintenance')
if (systemStore.systemSettings?.maintenance === 'on' && userStore.adminPerms === 0n) return next('/maintenance')
await systemStore.listSystemSettings()
if (systemStore.systemSettings?.maintenance === 'true' && userStore.adminPerms === 0n) return next('/maintenance')
}

next()
Expand Down
40 changes: 14 additions & 26 deletions apps/client/src/stores/system-settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { apiClient } from '../api/xhr-client.js'
import { useSystemSettingsStore } from './system-settings.js'

const listSystemSettings = vi.spyOn(apiClient.SystemSettings, 'listSystemSettings')
const upsertSystemSetting = vi.spyOn(apiClient.SystemSettings, 'upsertSystemSetting')
const upsertSystemSettings = vi.spyOn(apiClient.SystemSettings, 'upsertSystemSettings')

describe('system Settings Store', () => {
beforeEach(() => {
Expand All @@ -15,41 +15,29 @@ describe('system Settings Store', () => {
})

it('should get system settings list by api call', async () => {
const data = [
{ key: 'maintenace', value: 'on' },
{ key: 'theme', value: 'dsfr' },
{ key: 'organisation', value: 'miom' },
]
listSystemSettings.mockReturnValueOnce(Promise.resolve({ status: 200, body: data }))
const sytemSettings = { maintenace: 'true', theme: 'dsfr', organisation: 'miom' }
// @ts-expect-error TS2345
listSystemSettings.mockReturnValueOnce(Promise.resolve({ status: 200, body: sytemSettings }))
const systemSettingsStore = useSystemSettingsStore()

await systemSettingsStore.listSystemSettings()

expect(systemSettingsStore.systemSettings).toEqual(data)
expect(systemSettingsStore.systemSettings).toEqual(sytemSettings)
expect(listSystemSettings).toHaveBeenCalledTimes(1)
})

it('should upsert a system setting by api call', async () => {
const data = [
{ key: 'maintenace', value: 'on' },
{ key: 'theme', value: 'dsfr' },
{ key: 'organisation', value: 'miom' },
]
const newSystemSetting = { key: 'organisation', value: 'mj' }
const newData = [
{ key: 'maintenace', value: 'on' },
{ key: 'theme', value: 'dsfr' },
{ key: 'organisation', value: 'mj' },
]

upsertSystemSetting.mockReturnValueOnce(Promise.resolve({ status: 201, body: newSystemSetting }))
const sytemSettings = { maintenace: 'true', theme: 'dsfr', organisation: 'miom' }
const newSystemSettings = { maintenace: 'true', theme: 'dsfr', organisation: 'mj' }

// @ts-expect-error TS2345
upsertSystemSettings.mockReturnValueOnce(Promise.resolve({ status: 201, body: newSystemSettings }))
const systemSettingsStore = useSystemSettingsStore()
systemSettingsStore.systemSettings = data
systemSettingsStore.systemSettings = sytemSettings

const res = await systemSettingsStore.upsertSystemSetting(data)
await systemSettingsStore.upsertSystemSettings(sytemSettings)

expect(res).toEqual(newSystemSetting)
expect(systemSettingsStore.systemSettings).toEqual(newData)
expect(upsertSystemSetting).toHaveBeenCalledTimes(1)
expect(systemSettingsStore.systemSettings).toEqual(newSystemSettings)
expect(upsertSystemSettings).toHaveBeenCalledTimes(1)
})
})
21 changes: 9 additions & 12 deletions apps/client/src/stores/system-settings.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import { defineStore } from 'pinia'
import type {
SystemSettings,
UpsertSystemSettingsBody,
} from '@cpn-console/shared'
import { defineStore } from 'pinia'
import { apiClient, extractData } from '@/api/xhr-client.js'

export const useSystemSettingsStore = defineStore('systemSettings', () => {
const systemSettings = ref<SystemSettings>()

const listSystemSettings = async (key?: keyof SystemSettings) => {
systemSettings.value = await apiClient.SystemSettings.listSystemSettings({ query: { key } })
const listSystemSettings = async () => {
systemSettings.value = await apiClient.SystemSettings.listSystemSettings()
.then(response => extractData(response, 200))
}

// const upsertSystemSetting = async (newSystemSetting: UpsertSystemSettingBody) => {
// const res = await apiClient.SystemSettings.upsertSystemSetting({ body: newSystemSetting })
// .then(response => extractData(response, 201))
// systemSettings.value = systemSettings.value
// .toSpliced(systemSettings.value
// .findIndex(systemSetting => systemSetting.key === res.key), 1, res)
// return res
// }
const upsertSystemSettings = async (newSystemSetting: UpsertSystemSettingsBody) => {
systemSettings.value = await apiClient.SystemSettings.upsertSystemSettings({ body: newSystemSetting })
.then(response => extractData(response, 201))
}

return {
systemSettings,
listSystemSettings,
// upsertSystemSetting,
upsertSystemSettings,
}
})
3 changes: 1 addition & 2 deletions apps/client/src/views/DsoHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ const knowMoreBtn: Ref<KnowMoreBtn> = ref({
})
function setWindowLocation(to: string) {
// TODO
// @ts-ignore
// @ts-expect-error 2322
window.location = to
}
</script>
Expand Down
8 changes: 5 additions & 3 deletions apps/client/src/views/admin/ListPlugins.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ref } from 'vue'
import type { PluginSchema } from '@cpn-console/shared'
import { ref } from 'vue'
import { usePluginsConfigStore } from '@/stores/plugins.js'
import { useSnackbarStore } from '@/stores/snackbar'
Expand Down Expand Up @@ -53,7 +53,8 @@ onBeforeMount(() => {
reload()
})
function update(data: { value: string, key: string, plugin: string }) {
function update(data: { value: string | boolean, key: string, plugin: string }) {
if (typeof data.value === 'boolean') return
if (!updated.value[data.plugin]) updated.value[data.plugin] = {}
updated.value[data.plugin][data.key] = data.value
}
Expand Down Expand Up @@ -124,8 +125,9 @@ function update(data: { value: string, key: string, plugin: string }) {
// @ts-ignore si si il y a un placeholder
placeholder: item.placeholder || '',
disabled: !item.permissions.admin.write,
label: undefined,
}"
@update="(value: string) => update({ key: item.key, value, plugin: service.name })"
@update="(value: string | boolean) => update({ key: item.key, value, plugin: service.name })"
/>
</div>
</div>
Expand Down
Loading

0 comments on commit 9474fd9

Please sign in to comment.