mirror of
https://github.com/Canner/WrenAI.git
synced 2026-10-31 02:57:15 +08:00
feat(wren-ui/api): add view to manifest
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user