Skip to content

Commit

Permalink
a lot of pedestrian code
Browse files Browse the repository at this point in the history
  • Loading branch information
fontanierh committed Nov 16, 2023
1 parent 98d937f commit 3180f07
Show file tree
Hide file tree
Showing 7 changed files with 731 additions and 17 deletions.
20 changes: 11 additions & 9 deletions front/lib/core_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ export type CoreAPIDatabaseRow = {
content: Record<string, unknown>;
};

export type CoreAPIDatabaseSchema = Record<
string,
{
table: CoreAPIDatabaseTable;
schema: Record<string, "int" | "float" | "text" | "bool">;
}
>;

export const CoreAPI = {
async createProject(): Promise<CoreAPIResponse<{ project: Project }>> {
const response = await fetch(`${CORE_API}/projects`, {
Expand Down Expand Up @@ -1061,13 +1069,7 @@ export const CoreAPI = {
databaseId: string;
}): Promise<
CoreAPIResponse<{
schema: Record<
string,
{
table: CoreAPIDatabaseTable;
schema: Record<string, "int" | "float" | "text" | "bool">;
}
>;
schema: CoreAPIDatabaseSchema;
}>
> {
const response = await fetch(
Expand All @@ -1092,8 +1094,8 @@ export const CoreAPI = {
query: string;
}): Promise<
CoreAPIResponse<{
schema: Record<string, "int" | "float" | "text" | "bool">;
rows: Record<string, unknown>[];
schema: CoreAPIDatabaseSchema;
rows: CoreAPIDatabaseRow[];
}>
> {
const response = await fetch(
Expand Down
135 changes: 135 additions & 0 deletions front/pages/api/v1/w/[wId]/data_sources/[name]/databases/[id]/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { isLeft } from "fp-ts/lib/Either";
import * as t from "io-ts";
import * as reporter from "io-ts-reporters";
import { NextApiRequest, NextApiResponse } from "next";

import { getDataSource } from "@app/lib/api/data_sources";
import { Authenticator, getAPIKey } from "@app/lib/auth";
import {
CoreAPI,
CoreAPIDatabaseRow,
CoreAPIDatabaseSchema,
} from "@app/lib/core_api";
import { isDevelopmentOrDustWorkspace } from "@app/lib/development";
import logger from "@app/logger/logger";
import { apiError, withLogging } from "@app/logger/withlogging";

const GetDatabaseSchemaReqBodySchema = t.type({
query: t.string,
});

type QueryDatabaseSchemaResponseBody = {
schema: CoreAPIDatabaseSchema;
rows: CoreAPIDatabaseRow[];
};

async function handler(
req: NextApiRequest,
res: NextApiResponse<QueryDatabaseSchemaResponseBody>
): Promise<void> {
const keyRes = await getAPIKey(req);
if (keyRes.isErr()) {
return apiError(req, res, keyRes.error);
}

const { auth } = await Authenticator.fromKey(
keyRes.value,
req.query.wId as string
);

const owner = auth.workspace();
const plan = auth.plan();
if (!owner || !plan) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "workspace_not_found",
message: "The workspace you requested was not found.",
},
});
}

if (!isDevelopmentOrDustWorkspace(owner)) {
res.status(404).end();
return;
}

const dataSource = await getDataSource(auth, req.query.name as string);
if (!dataSource) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "data_source_not_found",
message: "The data source you requested was not found.",
},
});
}

const databaseId = req.query.id;
if (!databaseId || typeof databaseId !== "string") {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: "Invalid request query: id is required.",
},
});
}

switch (req.method) {
case "POST":
const bodyValidation = GetDatabaseSchemaReqBodySchema.decode(req.body);
if (isLeft(bodyValidation)) {
const pathError = reporter.formatValidationErrors(bodyValidation.left);
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: `Invalid request body: ${pathError}`,
},
});
}

const { query } = bodyValidation.right;

const queryRes = await CoreAPI.queryDatabase({
projectId: dataSource.dustAPIProjectId,
dataSourceName: dataSource.name,
databaseId,
query,
});
if (queryRes.isErr()) {
logger.error(
{
dataSourceName: dataSource.name,
workspaceId: owner.id,
databaseId,
error: queryRes.error,
},
"Failed to query database."
);
return apiError(req, res, {
status_code: 500,
api_error: {
type: "internal_server_error",
message: "Failed to query database.",
},
});
}

const { schema, rows } = queryRes.value;

return res.status(200).json({ schema, rows });

default:
return apiError(req, res, {
status_code: 405,
api_error: {
type: "method_not_supported_error",
message: "The method passed is not supported, GET is expected.",
},
});
}
}

export default withLogging(handler);
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { NextApiRequest, NextApiResponse } from "next";

import { getDataSource } from "@app/lib/api/data_sources";
import { Authenticator, getAPIKey } from "@app/lib/auth";
import { CoreAPI, CoreAPIDatabaseSchema } from "@app/lib/core_api";
import { isDevelopmentOrDustWorkspace } from "@app/lib/development";
import logger from "@app/logger/logger";
import { apiError, withLogging } from "@app/logger/withlogging";

type GetDatabaseSchemaResponseBody = {
schema: CoreAPIDatabaseSchema;
};

async function handler(
req: NextApiRequest,
res: NextApiResponse<GetDatabaseSchemaResponseBody>
): Promise<void> {
const keyRes = await getAPIKey(req);
if (keyRes.isErr()) {
return apiError(req, res, keyRes.error);
}

const { auth } = await Authenticator.fromKey(
keyRes.value,
req.query.wId as string
);

const owner = auth.workspace();
const plan = auth.plan();
if (!owner || !plan) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "workspace_not_found",
message: "The workspace you requested was not found.",
},
});
}

if (!isDevelopmentOrDustWorkspace(owner)) {
res.status(404).end();
return;
}

const dataSource = await getDataSource(auth, req.query.name as string);
if (!dataSource) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "data_source_not_found",
message: "The data source you requested was not found.",
},
});
}

const databaseId = req.query.id;
if (!databaseId || typeof databaseId !== "string") {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: "Invalid request query: id is required.",
},
});
}

switch (req.method) {
case "GET":
const schemaRes = await CoreAPI.getDatabaseSchema({
projectId: dataSource.dustAPIProjectId,
dataSourceName: dataSource.name,
databaseId,
});
if (schemaRes.isErr()) {
logger.error(
{
dataSourceName: dataSource.name,
workspaceId: owner.id,
databaseId,
error: schemaRes.error,
},
"Failed to get database schema."
);
return apiError(req, res, {
status_code: 500,
api_error: {
type: "internal_server_error",
message: "Failed to get database schema.",
},
});
}

const { schema } = schemaRes.value;

return res.status(200).json({ schema });

default:
return apiError(req, res, {
status_code: 405,
api_error: {
type: "method_not_supported_error",
message: "The method passed is not supported, GET is expected.",
},
});
}
}

export default withLogging(handler);
Loading

0 comments on commit 3180f07

Please sign in to comment.