Skip to content

Commit

Permalink
Save multiple connections
Browse files Browse the repository at this point in the history
  • Loading branch information
tbantle22 committed Nov 27, 2023
1 parent d0476fc commit 3d9da45
Show file tree
Hide file tree
Showing 23 changed files with 518 additions and 396 deletions.
21 changes: 9 additions & 12 deletions graphql-server/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,16 @@ type CommitList {
list: [Commit!]!
}

type DoltDatabaseDetails {
isDolt: Boolean!
hideDoltFeatures: Boolean!
}

type StoredState {
type DatabaseConnection {
connectionUrl: String!
useSSL: Boolean
name: String!
hideDoltFeatures: Boolean
useSSL: Boolean
}

type DatabaseState {
hasEnv: Boolean!
storedState: StoredState
type DoltDatabaseDetails {
isDolt: Boolean!
hideDoltFeatures: Boolean!
}

type DiffStat {
Expand Down Expand Up @@ -277,7 +273,7 @@ type Query {
defaultBranch(databaseName: String!): Branch
commits(offset: Int, databaseName: String!, refName: String, afterCommitId: String, twoDot: Boolean, excludingCommitsFromRefName: String): CommitList!
currentDatabase: String
databaseState: DatabaseState!
storedConnections: [DatabaseConnection!]!
databases: [String!]!
doltDatabaseDetails: DoltDatabaseDetails!
diffStat(databaseName: String!, fromRefName: String!, toRefName: String!, refName: String, type: CommitDiffType, tableName: String): DiffStat!
Expand Down Expand Up @@ -328,7 +324,8 @@ enum DiffRowType {
type Mutation {
createBranch(databaseName: String!, newBranchName: String!, fromRefName: String!): Branch!
deleteBranch(databaseName: String!, branchName: String!): Boolean!
addDatabaseConnection(connectionUrl: String, useEnv: Boolean, hideDoltFeatures: Boolean, useSSL: Boolean): String
addDatabaseConnection(connectionUrl: String!, name: String!, hideDoltFeatures: Boolean, useSSL: Boolean): String
removeDatabaseConnection(name: String!): Boolean!
createDatabase(databaseName: String!): Boolean!
resetDatabase: Boolean!
loadDataFile(tableName: String!, refName: String!, databaseName: String!, importOp: ImportOperation!, fileType: FileType!, file: Upload!, modifier: LoadDataModifier): Boolean!
Expand Down
4 changes: 2 additions & 2 deletions graphql-server/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { GraphQLModule } from "@nestjs/graphql";
import { TerminusModule } from "@nestjs/terminus";
import { DataSourceModule } from "./dataSources/dataSource.module";
import { FileStoreModule } from "./fileStore/fileStore.module";
import resolvers from "./resolvers";

@Module({
imports: [
ConfigModule.forRoot({ envFilePath: ".development.env" }),
GraphQLModule.forRoot<ApolloDriverConfig>({
autoSchemaFile: "schema.gql",
context: ctx => ctx,
driver: ApolloDriver,
}),
DataSourceModule,
FileStoreModule,
TerminusModule,
],
providers: resolvers,
Expand Down
16 changes: 16 additions & 0 deletions graphql-server/src/databases/database.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Field, ObjectType } from "@nestjs/graphql";

@ObjectType()
export class DatabaseConnection {
@Field()
connectionUrl: string;

@Field()
name: string;

@Field({ nullable: true })
hideDoltFeatures?: boolean;

@Field({ nullable: true })
useSSL?: boolean;
}
113 changes: 33 additions & 80 deletions graphql-server/src/databases/database.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ConfigService } from "@nestjs/config";
import {
Args,
ArgsType,
Expand All @@ -8,18 +7,18 @@ import {
Query,
Resolver,
} from "@nestjs/graphql";
import * as fs from "fs";
import { resolve } from "path";
import { DataSourceService } from "../dataSources/dataSource.service";
import { FileStoreService } from "../fileStore/fileStore.service";
import { DBArgs } from "../utils/commonTypes";
import { DatabaseConnection } from "./database.model";

@ArgsType()
class AddDatabaseConnectionArgs {
@Field({ nullable: true })
connectionUrl?: string;
@Field()
connectionUrl: string;

@Field({ nullable: true })
useEnv?: boolean;
@Field()
name: string;

@Field({ nullable: true })
hideDoltFeatures?: boolean;
Expand All @@ -37,32 +36,17 @@ class DoltDatabaseDetails {
hideDoltFeatures: boolean;
}

@ObjectType()
class StoredState {
@Field()
connectionUrl: string;

@Field({ nullable: true })
useSSL?: boolean;

@Field({ nullable: true })
hideDoltFeatures?: boolean;
}

@ObjectType()
class DatabaseState {
@ArgsType()
class RemoveDatabaseConnectionArgs {
@Field()
hasEnv: boolean;

@Field(_type => StoredState, { nullable: true })
storedState?: StoredState;
name: string;
}

@Resolver()
@Resolver(_of => DatabaseConnection)
export class DatabaseResolver {
constructor(
private readonly dss: DataSourceService,
private readonly configService: ConfigService,
private readonly fileStoreService: FileStoreService,
) {}

@Query(_returns => String, { nullable: true })
Expand All @@ -76,28 +60,9 @@ export class DatabaseResolver {
}
}

@Query(_returns => DatabaseState)
async databaseState(): Promise<DatabaseState> {
const hasEnv = !!this.configService.get("DATABASE_URL");
try {
const file = fs.readFileSync(
resolve(__dirname, "../../store/store.json"),
{
encoding: "utf8",
},
);
if (!file) {
return { hasEnv };
}
const parsed = JSON.parse(file);
return {
hasEnv,
storedState: parsed,
};
} catch (err) {
console.error("Error reading store.json:", err);
return { hasEnv };
}
@Query(_returns => [DatabaseConnection])
async storedConnections(): Promise<DatabaseConnection[]> {
return this.fileStoreService.getStore();
}

@Query(_returns => [String])
Expand Down Expand Up @@ -135,43 +100,31 @@ export class DatabaseResolver {
async addDatabaseConnection(
@Args() args: AddDatabaseConnectionArgs,
): Promise<string | undefined> {
if (args.useEnv) {
const url = this.configService.get("DATABASE_URL");
if (!url) throw new Error("DATABASE_URL not found in env");
const hideDoltFeatures = this.configService.get("HIDE_DOLT_FEATURES");
const useSSL = this.configService.get("USE_SSL");
const workbenchConfig = {
connectionUrl: url,
hideDoltFeatures: !!hideDoltFeatures && hideDoltFeatures === "true",
useSSL: useSSL !== undefined ? useSSL === "true" : true,
};
await this.dss.addDS(workbenchConfig);
} else if (args.connectionUrl) {
const workbenchConfig = {
connectionUrl: args.connectionUrl,
hideDoltFeatures: !!args.hideDoltFeatures,
useSSL: !!args.useSSL,
};
await this.dss.addDS(workbenchConfig);

if (!fs.existsSync(resolve(__dirname, "../../store"))) {
fs.mkdirSync(resolve(__dirname, "../../store"));
}

const stringified = JSON.stringify(workbenchConfig);
fs.writeFileSync(
resolve(__dirname, "../../store/store.json"),
stringified,
);
} else {
throw new Error("database url not provided");
}
const workbenchConfig = {
connectionUrl: args.connectionUrl,
hideDoltFeatures: !!args.hideDoltFeatures,
useSSL: !!args.useSSL,
};
await this.dss.addDS(workbenchConfig);

this.fileStoreService.addItemToStore({
...workbenchConfig,
name: args.name,
});

const db = await this.currentDatabase();
if (!db) return undefined;
return db;
}

@Mutation(_returns => Boolean)
async removeDatabaseConnection(
@Args() args: RemoveDatabaseConnectionArgs,
): Promise<boolean> {
this.fileStoreService.removeItemFromStore(args.name);
return true;
}

@Mutation(_returns => Boolean)
async createDatabase(@Args() args: DBArgs): Promise<boolean> {
const qr = this.dss.getQR();
Expand Down
5 changes: 5 additions & 0 deletions graphql-server/src/fileStore/fileStore.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Module } from "@nestjs/common";
import { FileStoreService } from "./fileStore.service";

@Module({ providers: [FileStoreService], exports: [FileStoreService] })
export class FileStoreModule {}
64 changes: 64 additions & 0 deletions graphql-server/src/fileStore/fileStore.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Injectable } from "@nestjs/common";
import * as fs from "fs";
import { resolve } from "path";
import { DatabaseConnection } from "../databases/database.model";

@Injectable()
export class FileStoreService {
constructor() {}

getStore(): Array<DatabaseConnection> {
try {
const file = fs.readFileSync(
resolve(__dirname, "../../store/store.json"),
{
encoding: "utf8",
},
);
if (!file) {
return [];
}
const parsed = JSON.parse(file);
return parsed;
} catch (err) {
console.error("Error reading store.json:", err);
return [];
}
}

addItemToStore(item: DatabaseConnection): void {
const store = this.getStore();

const existingItem = store.find(storeItem => storeItem.name === item.name);
if (existingItem) {
if (existingItem.connectionUrl === item.connectionUrl) return;
throw new Error("name already exists");
}

store.push(item);

if (!fs.existsSync(resolve(__dirname, "../../store"))) {
fs.mkdirSync(resolve(__dirname, "../../store"));
}

fs.writeFileSync(
resolve(__dirname, "../../store/store.json"),
JSON.stringify(store),
{
encoding: "utf8",
},
);
}

removeItemFromStore(name: string): void {
const store = this.getStore();
const newStore = store.filter(item => item.name !== name);
fs.writeFileSync(
resolve(__dirname, "../../store/store.json"),
JSON.stringify(newStore),
{
encoding: "utf8",
},
);
}
}
4 changes: 2 additions & 2 deletions web/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export default function Navbar(props: Props) {
<div className={css.inner}>
<div className={css.left}>
<DocsLink className={css.link}>Documentation</DocsLink>
<Link className={css.link} href="/configuration">
Configuration
<Link className={css.link} href="/connections">
Connections
</Link>
</div>

Expand Down
Loading

0 comments on commit 3d9da45

Please sign in to comment.