feat(app-config): persist relational virtual attrs
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
export async function up(knex) {
|
||||||
|
await knex.schema.alterTable('app_configs', (table) => {
|
||||||
|
table.boolean('can_connect').defaultTo(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
const appConfigs = await knex('app_configs').select('*');
|
||||||
|
|
||||||
|
for (const appConfig of appConfigs) {
|
||||||
|
const appAuthClients = await knex('app_auth_clients').where(
|
||||||
|
'app_key',
|
||||||
|
appConfig.key
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasSomeActiveAppAuthClients = !!appAuthClients?.some(
|
||||||
|
(appAuthClient) => appAuthClient.active
|
||||||
|
);
|
||||||
|
const shared = appConfig.shared;
|
||||||
|
const active = appConfig.disabled === false;
|
||||||
|
|
||||||
|
const canConnectConditions = [hasSomeActiveAppAuthClients, shared, active];
|
||||||
|
const canConnect = canConnectConditions.every(Boolean);
|
||||||
|
|
||||||
|
await knex('app_configs')
|
||||||
|
.where('id', appConfig.id)
|
||||||
|
.update({ can_connect: canConnect });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex) {
|
||||||
|
await knex.schema.alterTable('app_configs', (table) => {
|
||||||
|
table.dropColumn('can_connect');
|
||||||
|
});
|
||||||
|
}
|
@@ -2,6 +2,7 @@ import AES from 'crypto-js/aes.js';
|
|||||||
import enc from 'crypto-js/enc-utf8.js';
|
import enc from 'crypto-js/enc-utf8.js';
|
||||||
import appConfig from '../config/app.js';
|
import appConfig from '../config/app.js';
|
||||||
import Base from './base.js';
|
import Base from './base.js';
|
||||||
|
import AppConfig from './app-config.js';
|
||||||
|
|
||||||
class AppAuthClient extends Base {
|
class AppAuthClient extends Base {
|
||||||
static tableName = 'app_auth_clients';
|
static tableName = 'app_auth_clients';
|
||||||
@@ -21,6 +22,17 @@ class AppAuthClient extends Base {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static relationMappings = () => ({
|
||||||
|
appConfig: {
|
||||||
|
relation: Base.HasOneRelation,
|
||||||
|
modelClass: AppConfig,
|
||||||
|
join: {
|
||||||
|
from: 'app_auth_clients.app_key',
|
||||||
|
to: 'app_configs.key',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
encryptData() {
|
encryptData() {
|
||||||
if (!this.eligibleForEncryption()) return;
|
if (!this.eligibleForEncryption()) return;
|
||||||
|
|
||||||
@@ -48,6 +60,12 @@ class AppAuthClient extends Base {
|
|||||||
return this.authDefaults ? true : false;
|
return this.authDefaults ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async triggerAppConfigUpdate() {
|
||||||
|
const appConfig = await this.$relatedQuery('appConfig').select('*');
|
||||||
|
|
||||||
|
await appConfig.$query().patch({});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make another abstraction like beforeSave instead of using
|
// TODO: Make another abstraction like beforeSave instead of using
|
||||||
// beforeInsert and beforeUpdate separately for the same operation.
|
// beforeInsert and beforeUpdate separately for the same operation.
|
||||||
async $beforeInsert(queryContext) {
|
async $beforeInsert(queryContext) {
|
||||||
@@ -55,11 +73,23 @@ class AppAuthClient extends Base {
|
|||||||
this.encryptData();
|
this.encryptData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async $afterInsert(queryContext) {
|
||||||
|
await super.$afterInsert(queryContext);
|
||||||
|
|
||||||
|
await this.triggerAppConfigUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
async $beforeUpdate(opt, queryContext) {
|
async $beforeUpdate(opt, queryContext) {
|
||||||
await super.$beforeUpdate(opt, queryContext);
|
await super.$beforeUpdate(opt, queryContext);
|
||||||
this.encryptData();
|
this.encryptData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async $afterUpdate(queryContext) {
|
||||||
|
await super.$afterUpdate(queryContext);
|
||||||
|
|
||||||
|
await this.triggerAppConfigUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
async $afterFind() {
|
async $afterFind() {
|
||||||
this.decryptData();
|
this.decryptData();
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ class AppConfig extends Base {
|
|||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string', format: 'uuid' },
|
id: { type: 'string', format: 'uuid' },
|
||||||
key: { type: 'string' },
|
key: { type: 'string' },
|
||||||
|
canConnect: { type: 'boolean', default: false },
|
||||||
allowCustomConnection: { type: 'boolean', default: false },
|
allowCustomConnection: { type: 'boolean', default: false },
|
||||||
shared: { type: 'boolean', default: false },
|
shared: { type: 'boolean', default: false },
|
||||||
disabled: { type: 'boolean', default: false },
|
disabled: { type: 'boolean', default: false },
|
||||||
@@ -32,30 +33,39 @@ class AppConfig extends Base {
|
|||||||
});
|
});
|
||||||
|
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['canConnect', 'canCustomConnect'];
|
return ['canCustomConnect'];
|
||||||
}
|
}
|
||||||
|
|
||||||
get canCustomConnect() {
|
get canCustomConnect() {
|
||||||
return !this.disabled && this.allowCustomConnection;
|
return !this.disabled && this.allowCustomConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canConnect() {
|
|
||||||
const hasSomeActiveAppAuthClients = !!this.appAuthClients?.some(
|
|
||||||
(appAuthClient) => appAuthClient.active
|
|
||||||
);
|
|
||||||
const shared = this.shared;
|
|
||||||
const active = this.disabled === false;
|
|
||||||
|
|
||||||
const conditions = [hasSomeActiveAppAuthClients, shared, active];
|
|
||||||
|
|
||||||
return conditions.every(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getApp() {
|
async getApp() {
|
||||||
if (!this.key) return null;
|
if (!this.key) return null;
|
||||||
|
|
||||||
return await App.findOneByKey(this.key);
|
return await App.findOneByKey(this.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateCanConnectValue() {
|
||||||
|
const appAuthClients = await this.$relatedQuery('appAuthClients');
|
||||||
|
const hasSomeActiveAppAuthClients = !!appAuthClients?.some(
|
||||||
|
(appAuthClient) => appAuthClient.active
|
||||||
|
);
|
||||||
|
const shared = this.shared;
|
||||||
|
const active = this.disabled === false;
|
||||||
|
|
||||||
|
const conditions = [hasSomeActiveAppAuthClients, shared, active];
|
||||||
|
|
||||||
|
this.canConnect = conditions.every(Boolean);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
async $beforeUpdate(opt, queryContext) {
|
||||||
|
await super.$beforeUpdate(opt, queryContext);
|
||||||
|
|
||||||
|
await opt.old.updateCanConnectValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AppConfig;
|
export default AppConfig;
|
||||||
|
@@ -36,14 +36,6 @@ describe('AppConfig model', () => {
|
|||||||
expect(AppConfig.virtualAttributes).toMatchSnapshot();
|
expect(AppConfig.virtualAttributes).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getApp should return associated application', async () => {
|
|
||||||
const appConfig = await createAppConfig({ key: 'deepl' });
|
|
||||||
|
|
||||||
const app = await appConfig.getApp();
|
|
||||||
|
|
||||||
expect(app.key).toBe('deepl');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('canCustomConnect', () => {
|
describe('canCustomConnect', () => {
|
||||||
it('should return true when app is enabled and allows custom connection', async () => {
|
it('should return true when app is enabled and allows custom connection', async () => {
|
||||||
const appConfig = await createAppConfig({
|
const appConfig = await createAppConfig({
|
||||||
@@ -112,4 +104,12 @@ describe('AppConfig model', () => {
|
|||||||
expect(appConfig.canConnect).toBe(false);
|
expect(appConfig.canConnect).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getApp should return associated application', async () => {
|
||||||
|
const appConfig = await createAppConfig({ key: 'deepl' });
|
||||||
|
|
||||||
|
const app = await appConfig.getApp();
|
||||||
|
|
||||||
|
expect(app.key).toBe('deepl');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user