Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grant/bru-1049 #16

Merged
merged 9 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- CreateEnum
CREATE TYPE "RelationType" AS ENUM ('one_to_one', 'one_to_many', 'many_to_many');

-- CreateTable
CREATE TABLE "relation" (
"id" VARCHAR(32) NOT NULL DEFAULT CONCAT('rltn_', ksuid()),
"organization_id" TEXT NOT NULL,
"database_id" TEXT NOT NULL,
"type" "RelationType" NOT NULL,
"table_1" TEXT NOT NULL,
"table_2" TEXT NOT NULL,
"column_1" TEXT NOT NULL,
"column_2" TEXT NOT NULL,
"join_table" TEXT,
"join_column_1" TEXT,
"join_column_2" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"deleted_at" TIMESTAMP(3),

CONSTRAINT "relation_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "relation" ADD CONSTRAINT "relation_database_id_fkey" FOREIGN KEY ("database_id") REFERENCES "database"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "relation" ADD COLUMN "generated" BOOLEAN NOT NULL DEFAULT false;
32 changes: 29 additions & 3 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ generator client {
}

model database {
id String @id @default(dbgenerated("CONCAT('dbse_', ksuid())")) @db.VarChar(32)
id String @id @default(dbgenerated("CONCAT('dbse_', ksuid())")) @db.VarChar(32)
name String
external_name String
connection_url String
Expand All @@ -19,8 +19,9 @@ model database {
require_ssl Boolean
tables table[]
queries query[]
created_at DateTime @default(now())
updated_at DateTime @updatedAt
relations relation[]
created_at DateTime @default(now())
updated_at DateTime @updatedAt
deleted_at DateTime?
}

Expand Down Expand Up @@ -77,3 +78,28 @@ model query {
updated_at DateTime @updatedAt
deleted_at DateTime?
}

enum RelationType {
one_to_one
one_to_many
many_to_many
}

model relation {
id String @id @default(dbgenerated("CONCAT('rltn_', ksuid())")) @db.VarChar(32)
organization_id String
database_id String
database database @relation(fields: [database_id], references: [id])
type RelationType
generated Boolean @default(false)
table_1 String
table_2 String
column_1 String
column_2 String
join_table String?
join_column_1 String?
join_column_2 String?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
deleted_at DateTime?
}
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { UserQueriesModule } from "./user-queries/user-queries.module";
import * as _ from "lodash";
import { PostgresAdapterModule } from "./postgres-adapter/postgres-adapter.module";
import { AuthMiddleware } from "./shared/middlewares/auth.middleware";
import { RelationsModule } from "./relations/relations.module";

@Module({
imports: [
Expand All @@ -56,6 +57,7 @@ import { AuthMiddleware } from "./shared/middlewares/auth.middleware";
TablesModule,
UserQueriesModule,
PostgresAdapterModule,
RelationsModule,
],
controllers: [AppController],
providers: [
Expand Down
7 changes: 6 additions & 1 deletion backend/src/databases/databases.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { PrismaService } from "@/prisma/prisma.service";
import { Inject, Injectable, NotFoundException, forwardRef } from "@nestjs/common";
import {
Inject,
Injectable,
NotFoundException,
forwardRef,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { HttpRequestContextService } from "@/shared/http-request-context/http-request-context.service";
import { CreateDatabase, Database, UpdateDatabase } from "@/definitions";
Expand Down
2 changes: 2 additions & 0 deletions backend/src/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from "./database";
export * from "./table";
export * from "./user-query";
export * from "./postgres-adapter";
export * from "./relation";
export * from "./pipeline";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We likely want to change the name pipeline given this new context. What do you think? Is there clearly better name or is this good enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some options:

  • JsonQuery
  • StepQuery
  • PreQuery
  • PreSQL
  • ProtoSQL
  • ProtoQuery

154 changes: 49 additions & 105 deletions backend/src/definitions/pipeline.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
import { z } from "zod";
import { ExternalColumnSchema } from ".";

// SCHEMA INFERENCE:
export const InferredSchemaColumnSchema = ExternalColumnSchema.extend({
table: z.string(),
});
export type InferredSchemaColumn = z.infer<typeof InferredSchemaColumnSchema>;

// A relation used in a pipeline. Includes the API path to the relation from the base object defintiion and the object definition ID of the related object defintion
export const InferredSchemaRelationSchema = z.object({
api_path: z.string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is API path still relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not. Additional changes to these schemas and types (including removing unneeded ones) will be made as part of the query builder PR.

object_definition_id: z.string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely not relevant anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We'll have a new object for inferred schema relations as part of query builder.

relation_name: z.string(),
});
export type InferredSchemaRelation = z.infer<
typeof InferredSchemaRelationSchema
>;

// Includes a list of inferred pipeline output properties and the relations used in the pipeline
export const InferredSchemaSchema = z.object({
relations: z.array(InferredSchemaRelationSchema),
properties: z.array(InferredSchemaColumnSchema),
});
export type InferredSchema = z.infer<typeof InferredSchemaSchema>;

export const InferSchemaOutputSuccessSchema = z.object({
success: z.literal(true),
data: InferredSchemaSchema,
});
export type InferSchemaOutputSuccess = z.infer<
typeof InferSchemaOutputSuccessSchema
>;

export const InferSchemaOutputErrorSchema = z.object({
success: z.literal(false),
error: z.any(),
});
export type InferSchemaOutputError = z.infer<
typeof InferSchemaOutputErrorSchema
>;

export const InferSchemaOutputSchema = z.discriminatedUnion("success", [
InferSchemaOutputSuccessSchema,
InferSchemaOutputErrorSchema,
]);
export type InferSchemaOutput = z.infer<typeof InferSchemaOutputSchema>;

export enum ObjectPropertyTypeEnum {
string = "string",
Expand All @@ -19,13 +65,6 @@ export type FilterLogicalOperators = z.infer<
typeof FilterLogicalOperatorsEnumSchema
>;

export enum PipelineScopeEnum {
PRIVATE = "private",
ORGANIZATION = "organization",
}
export const PipelineScopeEnumSchema = z.nativeEnum(PipelineScopeEnum);
export type PipelineScope = z.infer<typeof PipelineScopeEnumSchema>;

export enum OperatorsEnum {
equal = "==",
notEqual = "!=",
Expand Down Expand Up @@ -60,7 +99,6 @@ export enum StepIdentifierEnum {
Filter = "filter",
Order = "order",
Take = "take",
Display = "display",
}
export const StepIdentifierEnumSchema = z.nativeEnum(StepIdentifierEnum);
export type StepIdentifier = z.infer<typeof StepIdentifierEnumSchema>;
Expand All @@ -69,14 +107,14 @@ export type StepIdentifier = z.infer<typeof StepIdentifierEnumSchema>;
// Select Step
export const SelectStepSchema = z.object({
type: z.literal(StepIdentifierEnum.Select),
select: z.array(z.string()).nonempty(),
select: z.array(InferredSchemaColumnSchema).nonempty(),
});
export type SelectStep = z.infer<typeof SelectStepSchema>;

// Aggregate Step
export const AggregateStepSchema = z.object({
type: z.literal(StepIdentifierEnum.Aggregate),
group: z.array(z.string()),
group: z.array(InferredSchemaColumnSchema),
operation: AggregationOperationsEnumSchema,
property: z.string(),
as: z.string(),
Expand Down Expand Up @@ -236,106 +274,12 @@ export const StepSchema = z.discriminatedUnion("type", [
]);
export type Step = z.infer<typeof StepSchema>;

// PIPELINE:
export const PipelinePermissionsSchema = z.object({
read: z.array(z.string()),
update: z.array(z.string()),
delete: z.array(z.string()),
});
export type PipelinePermissions = z.infer<typeof PipelinePermissionsSchema>;

export const PipelineSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
scope: PipelineScopeEnumSchema,
favorited_by: z.array(z.string()),
permissions: PipelinePermissionsSchema,
from: z.string().nonempty(),
from: z.string(),
steps: z.array(StepSchema),
creator_id: z.string(),
organization_id: z.string(),
created_at: z.coerce.date().optional(),
});
export type Pipeline = z.infer<typeof PipelineSchema>;

export const PartialPipelineSchema = z.object({
from: z.string().nonempty(),
steps: z.array(StepSchema),
});
export type PartialPipeline = z.infer<typeof PartialPipelineSchema>;

export const CreatePipelineSchema = PipelineSchema.omit({
id: true,
scope: true,
favorited_by: true,
permissions: true,
creator_id: true,
organization_id: true,
});
export type CreatePipeline = z.infer<typeof CreatePipelineSchema>;

export const UpdatePipelineSchema = PipelineSchema.partial().omit({
id: true,
scope: true,
favorited_by: true,
permissions: true,
});
export type UpdatePipeline = z.infer<typeof UpdatePipelineSchema>;

// SCHEMA INFERENCE:
export const InferredSchemaPropertySchema = z.object({
api_name: z.string(),
api_path: z.string(),
type: ObjectPropertyTypeEnumSchema,
name: z.string(),
description: z.string(),
object_definition_id: z.string().optional(),
relation_name: z.string().optional(), // Name of the relation used to bring the property into the pipeline, only present if the property exists on a relation
});
export type InferredSchemaProperty = z.infer<
typeof InferredSchemaPropertySchema
>;

// A relation used in a pipeline. Includes the API path to the relation from the base object defintiion and the object definition ID of the related object defintion
export const InferredSchemaRelationSchema = z.object({
api_path: z.string(),
object_definition_id: z.string(),
relation_name: z.string(),
});
export type InferredSchemaRelation = z.infer<
typeof InferredSchemaRelationSchema
>;

// Includes a list of inferred pipeline output properties and the relations used in the pipeline
export const InferredSchemaSchema = z.object({
relations: z.array(InferredSchemaRelationSchema),
properties: z.array(InferredSchemaPropertySchema),
});
export type InferredSchema = z.infer<typeof InferredSchemaSchema>;

export const InferSchemaOutputSuccessSchema = z.object({
success: z.literal(true),
data: InferredSchemaSchema,
});
export type InferSchemaOutputSuccess = z.infer<
typeof InferSchemaOutputSuccessSchema
>;

export const InferSchemaOutputErrorSchema = z.object({
success: z.literal(false),
error: z.any(),
});
export type InferSchemaOutputError = z.infer<
typeof InferSchemaOutputErrorSchema
>;

export const InferSchemaOutputSchema = z.discriminatedUnion("success", [
InferSchemaOutputSuccessSchema,
InferSchemaOutputErrorSchema,
]);
export type InferSchemaOutput = z.infer<typeof InferSchemaOutputSchema>;

// STEP VALIDATION:
export const StepValidationOutputSuccessSchema = z.object({
success: z.literal(true),
Expand Down
49 changes: 49 additions & 0 deletions backend/src/definitions/relation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { z } from "zod";

export enum RelationTypeEnum {
OneToOne = "one_to_one",
OneToMany = "one_to_many",
ManyToMany = "many_to_many",
}
export const RelationTypeEnumSchema = z.nativeEnum(RelationTypeEnum);
export type RelationType = z.infer<typeof RelationTypeEnumSchema>;

export const RelationSchema = z.object({
id: z.string(),
organization_id: z.string(),
generated: z.boolean(),
database_id: z.string(),
type: RelationTypeEnumSchema,
table_1: z.string(),
table_2: z.string(),
column_1: z.string(),
column_2: z.string(),
join_table: z.string().optional().nullable(),
join_column_1: z.string().optional().nullable(),
join_column_2: z.string().optional().nullable(),
created_at: z.date(),
updated_at: z.date(),
deleted_at: z.date().optional().nullable(),
});
export type Relation = z.infer<typeof RelationSchema>;

export const CreateRelationSchema = RelationSchema.omit({
id: true,
organization_id: true,
generated: true,
created_at: true,
updated_at: true,
deleted_at: true,
});
export type CreateRelation = z.infer<typeof CreateRelationSchema>;

export const UpdateRelationSchema = RelationSchema.omit({
id: true,
database_id: true,
organization_id: true,
generated: true,
created_at: true,
updated_at: true,
deleted_at: true,
}).partial();
export type UpdateRelation = z.infer<typeof UpdateRelationSchema>;
2 changes: 1 addition & 1 deletion backend/src/definitions/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const TableConfigurationSchema = z.object({
primary_key: z.string().optional(),
title_property: z.string().optional(),
hidden_columns: z.array(z.string()).optional(),
ordered_columns: z.array(z.string()).optional()
ordered_columns: z.array(z.string()).optional(),
});
export type TableConfiguration = z.infer<typeof TableConfigurationSchema>;

Expand Down
Loading
Loading