Skip to content

Commit

Permalink
fix error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
TristenHarr committed Mar 22, 2024
1 parent f9d5995 commit ca77c24
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 44 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ This changelog documents changes between release tags.
## [Unreleased]
Upcoming changes for the next versioned release.

## [0.0.10]
* Fix Error Handling so messages pass through to GraphQL engine

## [0.0.9]
* Fix bug in filter joins

Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,24 @@

The Turso Data Connector allows for connecting to a libSQL database. This uses the [Typescript Data Connector SDK](https://github.com/hasura/ndc-sdk-typescript) and implements the [Data Connector Spec](https://github.com/hasura/ndc-spec).

In order to use this connector you will need a Turso database setup. This connector currently only supports querying.
In order to use this connector you will need a Turso database setup.

This connector supports:

Querying:

Relationships ✅
Variables ✅

Expirimental Mutations:

This connector has experimental mutation support for insert, delete, and update.

VIDEO OF QUICKSTART

QUICKSTART DOCUMENTATION

This connector currently only supports querying.

## Before you get started
It is recommended that you:
Expand Down
4 changes: 2 additions & 2 deletions connector-definition/connector-metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
packagingDefinition:
type: PrebuiltDockerImage
dockerImage: ghcr.io/hasura/ndc-turso:v0.0.9
dockerImage: ghcr.io/hasura/ndc-turso:v0.0.10
supportedEnvironmentVariables:
- name: TURSO_URL
description: The url for the Turso database
- name: TURSO_AUTH_TOKEN
description: The turso auth token
commands:
update: docker run --rm -e TURSO_URL="$TURSO_URL" -e TURSO_AUTH_TOKEN="$TURSO_AUTH_TOKEN" -v "$HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH":/etc/connector ghcr.io/hasura/ndc-turso:v0.0.9 update
update: docker run --rm -e TURSO_URL="$TURSO_URL" -e TURSO_AUTH_TOKEN="$TURSO_AUTH_TOKEN" -v "$HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH":/etc/connector ghcr.io/hasura/ndc-turso:v0.0.10 update
dockerComposeWatch:
- path: ./
target: /etc/connector
Expand Down
1 change: 0 additions & 1 deletion generate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ async function main() {
object_types: object_types
},
};

await writeFile(`/etc/connector/config.json`, JSON.stringify(res));
}

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ndc-turso",
"version": "0.0.9",
"version": "0.0.10",
"main": "index.js",
"author": "Tristen Harr",
"scripts": {},
Expand Down
1 change: 0 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CapabilitiesResponse, ObjectField, ObjectType, ScalarType } from "@hasura/ndc-sdk-typescript";
import { JSONSchemaObject } from "@json-schema-tools/meta-schema";
export const MAX_32_INT: number = 2147483647;
export const CAPABILITIES_RESPONSE: CapabilitiesResponse = {
version: "^0.1.0",
Expand Down
26 changes: 13 additions & 13 deletions src/handlers/mutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MutationOperation, MutationOperationResults, MutationRequest, MutationResponse, NotSupported } from "@hasura/ndc-sdk-typescript";
import { Conflict, Forbidden, MutationOperation, MutationOperationResults, MutationRequest, MutationResponse, NotSupported } from "@hasura/ndc-sdk-typescript";
import { Configuration, State } from "..";
import { InArgs, InStatement, ResultSet } from "@libsql/client/.";

Expand Down Expand Up @@ -57,14 +57,14 @@ export async function do_mutation(configuration: Configuration, state: State, mu
let operation_results: MutationOperationResults[] = [];
for (let op of mutation.operations){
if (op.type !== "procedure"){
throw new NotSupported("Not implemented yet.", {});
throw new Forbidden("Not implemented yet.", {});
} else {
procedures.push(op);
}
}
for (let procedure of procedures){
if (procedure.type !== "procedure"){
throw new NotSupported("Not implemented yet.", {});
throw new Forbidden("Not implemented yet.", {});
}
if (procedure.name === "sync"){
const client = state.client;
Expand Down Expand Up @@ -92,11 +92,11 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing insert operation:", error);
throw new Error("Failed to execute insert operation.");
throw new Conflict("Failed to execute insert operation.", {error: `${error}`});
}
} else {
console.log("No SQL statement generated for insert operation.");
throw new NotSupported("No data provided for insert operation.", {});
throw new Forbidden("No data provided for insert operation.", {});
}
} else if (procedure.name.startsWith("delete_") && procedure.name.endsWith("_by_pk")) {
const table: string = procedure.name.slice("delete_".length, -"_by_pk".length);
Expand All @@ -112,7 +112,7 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing delete operation:", error);
throw new Error("Failed to execute delete operation.");
throw new Conflict("Failed to execute delete operation.", {error: `${error}`});
}
} else if (procedure.name.startsWith("update_") && procedure.name.endsWith("_by_pk")){
const table: string = procedure.name.slice("update_".length, -"_by_pk".length);
Expand All @@ -131,7 +131,7 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing update operation:", error);
throw new Error("Failed to execute update operation.");
throw new Conflict("Failed to execute update operation.");
}
} else if (procedure.name.startsWith("update_") && procedure.name.endsWith("_many")){
const table: string = procedure.name.slice("update_".length, -"_many".length);
Expand All @@ -140,7 +140,7 @@ export async function do_mutation(configuration: Configuration, state: State, mu
const incArray = procedure.arguments._inc_array as any[] || [];

if (pkColumnsArray.length !== setArray.length || pkColumnsArray.length !== incArray.length) {
throw new Error("Arrays must be of the same length.");
throw new Forbidden("Arrays must be of the same length.");
}

const statements: InStatement[] = pkColumnsArray.map((pkColumns, index) => {
Expand All @@ -159,7 +159,7 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing batch update operation:", error);
throw new Error("Failed to execute batch update operation.");
throw new Conflict("Failed to execute batch update operation.", {error: `${error}`});
}
} else if (procedure.name.startsWith("delete_") && procedure.name.endsWith("_many")) {
const table = procedure.name.slice("delete_".length, -"_many".length);
Expand All @@ -180,14 +180,14 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing batch delete operation:", error);
throw new Error("Failed to execute batch delete operation.");
throw new Conflict("Failed to execute batch delete operation.", {error: `${error}`});
}
} else if (procedure.name.startsWith("insert_") && procedure.name.endsWith("_many")){
const table = procedure.name.slice("insert_".length, -"_many".length);
const data = procedure.arguments.objects as { [key: string]: any }[]; // Assuming `objects` is the name of the argument containing the records to insert

if (!data || data.length === 0) {
throw new NotSupported("No data provided for insert_many operation.", {});
throw new Forbidden("No data provided for insert_many operation.", {});
}

const [sql, values] = buildInsertSql(table, data);
Expand All @@ -203,11 +203,11 @@ export async function do_mutation(configuration: Configuration, state: State, mu
});
} catch (error) {
console.error("Error executing insert_many operation:", error);
throw new Error("Failed to execute insert_many operation.");
throw new Conflict("Failed to execute insert_many operation.", {error: `${error}`});
}
} else {
console.log("NOT COVERED");
throw new NotSupported("Not implemented yet.", {});
throw new Forbidden("Not implemented yet.", {});
}
}
}
Expand Down
35 changes: 19 additions & 16 deletions src/handlers/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import {
Expression,
QueryResponse,
RowSet,
BadRequest,
NotSupported,
InternalServerError,
Conflict,
Forbidden,
Query,
Relationship,
} from "@hasura/ndc-sdk-typescript";
Expand Down Expand Up @@ -59,7 +58,7 @@ function build_where(
prefix: string
): string {
if (!config.config){
throw new InternalServerError("Internal Server Error", {});
throw new Forbidden("Internal Server Error", {});
}
let sql = "";
switch (expression.type) {
Expand All @@ -69,7 +68,7 @@ function build_where(
sql = `${expression.column.name} IS NULL`;
break;
default:
throw new BadRequest("Unknown Unary Comparison Operator", {
throw new Conflict("Unknown Unary Comparison Operator", {
"Unknown Operator": "This should never happen.",
});
}
Expand All @@ -85,9 +84,9 @@ function build_where(
}
break;
case "column":
throw new BadRequest("Not implemented", {});
throw new Forbidden("Not implemented", {});
default:
throw new BadRequest("Unknown Binary Comparison Value Type", {});
throw new Conflict("Unknown Binary Comparison Value Type", {});
}
const columnName = expression.column.name;

Expand Down Expand Up @@ -178,7 +177,7 @@ function build_where(
sql = `${escape_double(prefix)}.${escape_double(columnName)} <= ?`;
break;
default:
throw new BadRequest("Binary Comparison Custom Operator not implemented", {});
throw new Forbidden("Binary Comparison Custom Operator not implemented", {});
}
break;
case "and":
Expand Down Expand Up @@ -211,9 +210,9 @@ function build_where(
break;
case "exists":
// EXISTS
throw new BadRequest("Not implemented", {});
throw new Forbidden("Not implemented", {});
default:
throw new BadRequest("Unknown Expression Type!", {});
throw new Forbidden("Unknown Expression Type!", {});
}
return sql;
}
Expand All @@ -240,7 +239,7 @@ function build_query(
let where_conditions = ["WHERE 1"];
if (query.aggregates) {
// TODO: Add each aggregate to collectRows
throw new NotSupported("Aggregates not implemented yet!", {});
throw new Forbidden("Aggregates not implemented yet!", {});
}
if (query.fields) {
for (let [field_name, field_value] of Object.entries(query.fields)) {
Expand All @@ -267,7 +266,7 @@ function build_query(
path.pop(); // POST-ORDER search stack pop!
break;
default:
throw new InternalServerError("The types tricked me. 😭", {});
throw new Forbidden("The types tricked me. 😭", {});
}
}
}
Expand Down Expand Up @@ -305,17 +304,17 @@ function build_query(
);
break;
case "single_column_aggregate":
throw new NotSupported(
throw new Forbidden(
"Single Column Aggregate not supported yet",
{}
);
case "star_count_aggregate":
throw new NotSupported(
throw new Forbidden(
"Single Column Aggregate not supported yet",
{}
);
default:
throw new BadRequest("The types lied 😭", {});
throw new Forbidden("The types lied 😭", {});
}
}
if (order_elems.length > 0) {
Expand Down Expand Up @@ -363,7 +362,7 @@ export async function plan_queries(
query: QueryRequest
): Promise<SQLiteQuery[]> {
if (!configuration.config) {
throw new InternalServerError("Connector is not properly configured", {});
throw new Forbidden("Connector is not properly configured", {});
}

let query_plan: SQLiteQuery[];
Expand Down Expand Up @@ -402,13 +401,17 @@ async function perform_query(
state: State,
query_plans: SQLiteQuery[]
): Promise<QueryResponse> {
try {
const client = state.client;
const results = await client.batch(query_plans, "read");
let res = results.map((r) => {
let row_set = JSON.parse(r.rows[0].data as string) as RowSet;
return row_set;
});
return res;
} catch (e) {
throw new Forbidden("Failed to perform Query", {error: `${e}`});
};
}

export async function do_query(
Expand Down
14 changes: 7 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ExplainResponse,
start,
Connector,
InternalServerError,
Forbidden
} from "@hasura/ndc-sdk-typescript";
import { CAPABILITIES_RESPONSE } from "./constants";
import { do_query } from "./handlers/query";
Expand Down Expand Up @@ -79,7 +79,7 @@ const connector: Connector<Configuration, State> = {
return Promise.resolve(configObject);
} catch (error) {
console.error("Failed to parse configuration:", error);
throw new InternalServerError(
throw new Forbidden(
"Internal Server Error, server configuration is invalid",
{}
);
Expand Down Expand Up @@ -125,7 +125,7 @@ const connector: Connector<Configuration, State> = {
*/
async getSchema(configuration: Configuration): Promise<SchemaResponse> {
if (!configuration.config) {
throw new InternalServerError(
throw new Forbidden(
"Internal Server Error, server configuration is invalid",
{}
);
Expand All @@ -148,7 +148,7 @@ const connector: Connector<Configuration, State> = {
request: QueryRequest
): Promise<ExplainResponse> {
if (!configuration.config) {
throw new InternalServerError(
throw new Forbidden(
"Internal Server Error, server configuration is invalid",
{}
);
Expand All @@ -168,12 +168,12 @@ const connector: Connector<Configuration, State> = {
request: MutationRequest
): Promise<ExplainResponse> {
if (!configuration.config) {
throw new InternalServerError(
throw new Forbidden(
"Internal Server Error, server configuration is invalid",
{}
);
}
throw new InternalServerError("Not implemented", {});
throw new Forbidden("Not implemented", {});
},

/**
Expand All @@ -191,7 +191,7 @@ const connector: Connector<Configuration, State> = {
request: QueryRequest
): Promise<QueryResponse> {
if (!configuration.config) {
throw new InternalServerError(
throw new Forbidden(
"Internal Server Error, server configuration is invalid",
{}
);
Expand Down

0 comments on commit ca77c24

Please sign in to comment.