feat(wren-ui/api): add view to manifest

This commit is contained in:
william chang(張仲威)
2024-04-12 14:33:35 +08:00
parent e7a69f0042
commit b55b3e17bc
4 changed files with 232 additions and 6 deletions
+25 -1
View File
@@ -1,5 +1,11 @@
import { isEmpty } from 'lodash';
import { Model, ModelColumn, Project, RelationInfo } from '../repositories';
import {
Model,
ModelColumn,
Project,
RelationInfo,
View,
} from '../repositories';
import { Manifest, ModelMDL } from './type';
import { getLogger } from '@server/utils';
@@ -11,6 +17,7 @@ export interface MDLBuilderBuildFromOptions {
models: Model[];
columns?: ModelColumn[];
relations?: RelationInfo[];
views: View[];
relatedModels?: Model[];
relatedColumns?: ModelColumn[];
relatedRelations?: RelationInfo[];
@@ -28,6 +35,7 @@ export class MDLBuilder implements IMDLBuilder {
private readonly models: Model[];
private readonly columns: ModelColumn[];
private readonly relations: RelationInfo[];
private readonly views: View[];
// related models, columns, and relations are used as the reference to build calculatedField expression or other
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -43,6 +51,7 @@ export class MDLBuilder implements IMDLBuilder {
models,
columns,
relations,
views,
relatedModels,
relatedColumns,
relatedRelations,
@@ -51,6 +60,7 @@ export class MDLBuilder implements IMDLBuilder {
this.models = models;
this.columns = columns;
this.relations = relations;
this.views = views;
this.relatedModels = relatedModels;
this.relatedColumns = relatedColumns;
this.relatedRelations = relatedRelations;
@@ -64,6 +74,7 @@ export class MDLBuilder implements IMDLBuilder {
this.addModel();
this.addColumn();
this.addRelation();
this.addView();
return this.getManifest();
}
@@ -88,6 +99,19 @@ export class MDLBuilder implements IMDLBuilder {
});
}
public addView(): void {
if (!isEmpty(this.manifest.views)) {
return;
}
this.manifest.views = this.views.map((view: View) => {
return {
name: view.name,
statement: view.statement,
properties: JSON.parse(view.properties),
};
});
}
public addColumn(): void {
// should addModel first
if (isEmpty(this.manifest.models)) {
@@ -1,6 +1,6 @@
import { Model, Project, ModelColumn, RelationInfo } from '../../repositories';
import { Model, Project, ModelColumn, RelationInfo, View } from '../../repositories';
import { MDLBuilder, MDLBuilderBuildFromOptions } from '../mdlBuilder';
import { ModelMDL, RelationMDL } from '../type';
import { ModelMDL, RelationMDL, ViewMDL } from '../type';
describe('MDLBuilder', () => {
let mdlBuilder: MDLBuilder;
@@ -144,6 +144,7 @@ describe('MDLBuilder', () => {
relationship: 'OrderCustomer',
properties: null,
notNull: false,
properties: null,
},
],
cached: false,
@@ -192,4 +193,198 @@ describe('MDLBuilder', () => {
expect(manifest.relationships).toEqual(expectedRelationships);
});
});
it('should return a manifest with models & columns & relations & views.', () => {
// Arrange
const project = {
id: 1,
type: 'bigquery',
displayName: 'my project',
projectId: 'bq-project-id',
datasetId: 'my-dataset',
credentials: 'my-credential',
catalog: 'tbd',
schema: 'tbd',
sampleDataset: null,
} as Project;
const models = [
{
id: 1,
projectId: 1,
displayName: 'order',
sourceTableName: 'order',
referenceName: 'order',
refSql: 'SELECT * FROM order',
cached: false,
refreshTime: null,
properties: JSON.stringify({ description: 'foo table' }),
},
{
id: 2,
projectId: 1,
displayName: 'customer',
sourceTableName: 'customer',
referenceName: 'customer',
refSql: 'SELECT * FROM customer',
cached: false,
refreshTime: null,
properties: null,
},
] as Model[];
const columns = [
{
id: 1,
modelId: 1,
isCalculated: false,
displayName: 'orderKey',
referenceName: 'orderKey',
sourceColumnName: 'orderKey',
aggregation: null,
lineage: null,
diagram: null,
customExpression: null,
type: 'STRING',
notNull: true,
isPk: true,
properties: JSON.stringify({ description: 'bar' }),
},
{
id: 2,
modelId: 2,
isCalculated: false,
displayName: 'orderKey',
referenceName: 'orderKey',
sourceColumnName: 'orderKey',
aggregation: null,
lineage: null,
diagram: null,
customExpression: null,
type: 'STRING',
notNull: true,
isPk: false,
properties: null,
},
] as ModelColumn[];
const relations = [
{
id: 1,
projectId: 1,
name: 'OrderCustomer',
fromColumnId: 1,
toColumnId: 2,
joinType: 'oneToMany',
fromModelId: 1,
fromModelName: 'order',
fromColumnName: 'orderKey',
toModelId: 2,
toModelName: 'customer',
toColumnName: 'orderKey',
},
] as RelationInfo[];
const views = [
{
id: 1,
projectId: 1,
name: 'view',
statement: 'select * from order',
cached: false,
properties: JSON.stringify({
description: 'foo view',
displayName: 'view',
}),
},
] as View[];
const builderOptions = {
project,
models,
views,
columns,
relations,
relatedModels: [],
relatedColumns: [],
relatedRelations: [],
} as MDLBuilderBuildFromOptions;
mdlBuilder = new MDLBuilder(builderOptions);
// Act
const manifest = mdlBuilder.build();
// Assert
const expectedModels = [
{
name: 'order',
refSql: 'SELECT * FROM order',
columns: [
{
name: 'orderKey',
expression: '',
type: 'STRING',
isCalculated: false,
notNull: true,
properties: { description: 'bar' },
},
{
name: 'customer',
type: 'customer',
isCalculated: false,
relationship: 'OrderCustomer',
notNull: false,
properties: null,
},
],
cached: false,
refreshTime: null,
primaryKey: 'orderKey',
properties: { description: 'foo table' },
},
{
name: 'customer',
refSql: 'SELECT * FROM customer',
columns: [
{
name: 'orderKey',
expression: '',
type: 'STRING',
isCalculated: false,
notNull: true,
properties: null,
},
{
name: 'order',
type: 'order',
isCalculated: false,
relationship: 'OrderCustomer',
notNull: false,
properties: null,
},
],
primaryKey: '',
cached: false,
refreshTime: null,
properties: null,
},
] as ModelMDL[];
const expectedRelationships = [
{
name: 'OrderCustomer',
models: ['order', 'customer'],
joinType: 'oneToMany',
condition: 'order.orderKey = customer.orderKey',
},
] as RelationMDL[];
const expectedViews = [
{
name: 'view',
statement: 'select * from order',
properties: { description: 'foo view', displayName: 'view' },
},
] as ViewMDL[];
expect(manifest.models).toEqual(expectedModels);
expect(manifest.relationships).toEqual(expectedRelationships);
expect(manifest.views).toEqual(expectedViews);
});
});
+3 -3
View File
@@ -52,11 +52,11 @@ export interface EnumDefinition {
};
}
export interface View {
export interface ViewMDL {
name: string;
statement: string;
description?: string;
properties?: {
displayName: string;
description?: string;
};
}
@@ -67,5 +67,5 @@ export interface Manifest {
models?: ModelMDL[];
relationships?: RelationMDL[];
enumDefinitions?: EnumDefinition[];
views?: View[];
views?: ViewMDL[];
}
@@ -4,6 +4,7 @@ import {
IModelRepository,
IProjectRepository,
IRelationRepository,
IViewRepository,
} from '../repositories';
import { Manifest } from '../mdl/type';
export interface IMDLService {
@@ -13,22 +14,26 @@ export interface IMDLService {
export class MDLService implements IMDLService {
private projectRepository: IProjectRepository;
private modelRepository: IModelRepository;
private viewRepository: IViewRepository;
private modelColumnRepository: IModelColumnRepository;
private relationRepository: IRelationRepository;
constructor({
projectRepository,
modelRepository,
viewRepository,
modelColumnRepository,
relationRepository,
}: {
projectRepository: IProjectRepository;
modelRepository: IModelRepository;
viewRepository: IViewRepository;
modelColumnRepository: IModelColumnRepository;
relationRepository: IRelationRepository;
}) {
this.projectRepository = projectRepository;
this.modelRepository = modelRepository;
this.viewRepository = viewRepository;
this.modelColumnRepository = modelColumnRepository;
this.relationRepository = relationRepository;
}
@@ -37,6 +42,7 @@ export class MDLService implements IMDLService {
const project = await this.projectRepository.getCurrentProject();
const projectId = project.id;
const models = await this.modelRepository.findAllBy({ projectId });
const views = await this.viewRepository.findAllBy({ projectId });
const modelIds = models.map((m) => m.id);
const columns =
await this.modelColumnRepository.findColumnsByModelIds(modelIds);
@@ -49,6 +55,7 @@ export class MDLService implements IMDLService {
const mdlBuilder = new MDLBuilder({
project,
models,
views,
columns,
relations,
relatedModels,