Skip to content

Commit

Permalink
Revert "Revert "feat(structured data): remaining front API endpoints"" (
Browse files Browse the repository at this point in the history
#2581)

* Revert "Revert "feat(structured data): remaining front API endpoints (#2574)" (#2579)"

This reverts commit 68e99aa.

* fix query params dyn routes
  • Loading branch information
fontanierh authored Nov 17, 2023
1 parent 0777d2b commit d888aa0
Show file tree
Hide file tree
Showing 8 changed files with 1,000 additions and 12 deletions.
26 changes: 14 additions & 12 deletions front/lib/core_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,21 +119,29 @@ export type CoreAPIDatabase = {
name: string;
};

type CoreAPIDatabaseTable = {
export type CoreAPIDatabaseTable = {
created: number;
database_id: string;
table_id: string;
name: string;
description: string;
};

type CoreAPIDatabaseRow = {
export type CoreAPIDatabaseRow = {
created: number;
table_id: string;
row_id: string;
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 @@ -905,7 +913,7 @@ export const CoreAPI = {
tableId: string;
name: string;
description: string;
}): Promise<CoreAPIResponse<{ database: CoreAPIDatabase }>> {
}): Promise<CoreAPIResponse<{ table: CoreAPIDatabaseTable }>> {
const response = await fetch(
`${CORE_API}/projects/${projectId}/data_sources/${dataSourceName}/databases/${databaseId}/tables`,
{
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { NextApiRequest, NextApiResponse } from "next";

import { getDataSource } from "@app/lib/api/data_sources";
import { Authenticator, getAPIKey } from "@app/lib/auth";
import { CoreAPI, CoreAPIDatabase } 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 GetDatabaseResponseBody = {
database: CoreAPIDatabase;
};

async function handler(
req: NextApiRequest,
res: NextApiResponse<GetDatabaseResponseBody>
): 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.dId;
if (!databaseId || typeof databaseId !== "string") {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: "The database id is missing.",
},
});
}

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

const { database } = databaseRes.value;

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

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,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.dId;
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);
Loading

0 comments on commit d888aa0

Please sign in to comment.