mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-13 11:07:11 +03:00
Sqlite reference agent
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5263 Co-authored-by: Daniel Chambers <1214352+daniel-chambers@users.noreply.github.com> GitOrigin-RevId: fb5cf3cb80ab59cd5ee0d064e3f776c062856aa3
This commit is contained in:
parent
162e51c668
commit
fc907201a0
3
dc-agents/sqlite/.gitignore
vendored
Normal file
3
dc-agents/sqlite/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
./*.sqlite
|
1
dc-agents/sqlite/.nvmrc
Normal file
1
dc-agents/sqlite/.nvmrc
Normal file
@ -0,0 +1 @@
|
||||
lts/gallium
|
21
dc-agents/sqlite/Dockerfile
Normal file
21
dc-agents/sqlite/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
FROM node:16-alpine
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
|
||||
RUN npm ci
|
||||
|
||||
COPY tsconfig.json .
|
||||
COPY src src
|
||||
|
||||
# This is just to ensure everything compiles ahead of time.
|
||||
# We'll actually run using ts-node to ensure we get TypesScript
|
||||
# stack traces if something fails at runtime.
|
||||
RUN npm run typecheck
|
||||
|
||||
EXPOSE 8100
|
||||
|
||||
# We don't bother doing typechecking when we run (only TS->JS transpiling)
|
||||
# because we checked it above already. This uses less memory at runtime.
|
||||
CMD [ "npm", "run", "--silent", "start-no-typecheck" ]
|
150
dc-agents/sqlite/README.md
Normal file
150
dc-agents/sqlite/README.md
Normal file
@ -0,0 +1,150 @@
|
||||
# Data Connector Agent for SQLite
|
||||
|
||||
This directory contains an SQLite implementation of a data connector agent.
|
||||
It can use local SQLite database files as referenced by the "db" config field.
|
||||
|
||||
## Capabilities
|
||||
|
||||
The SQLite agent currently supports the following capabilities:
|
||||
|
||||
* [x] GraphQL Schema
|
||||
* [x] GraphQL Queries
|
||||
* [ ] GraphQL Mutations
|
||||
* [x] Relationships
|
||||
* [x] Aggregations
|
||||
* [ ] Exposing Foreign-Key Information
|
||||
* [ ] Subscriptions
|
||||
* [ ] Streaming Subscriptions
|
||||
|
||||
Note: You are able to get detailed metadata about the agent's capabilities by
|
||||
`GET`ting the `/capabilities` endpoint of the running agent.
|
||||
|
||||
## Requirements
|
||||
|
||||
* NodeJS 16
|
||||
* SQLite `>= 3.38.0` or compiled in JSON support
|
||||
* Required for the json_group_array() and json_group_object() aggregate SQL functions
|
||||
* https://www.sqlite.org/json1.html#jgrouparray
|
||||
|
||||
## Build & Run
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
Or a simple dev-loop via `entr`:
|
||||
|
||||
```sh
|
||||
echo src/**/*.ts | xargs -n1 echo | DB_READONLY=y entr -r npm run start
|
||||
```
|
||||
|
||||
## Options / Environment Variables
|
||||
|
||||
* ENV: `PORT=[INT]` - Port for agent to listen on. 8100 by default.
|
||||
* ENV: `PERMISSIVE_CORS={1|true|yes}` - Allows all requests - Useful for testing with SwaggerUI. Turn off on production.
|
||||
* ENV: `DB_CREATE={1|true|yes}` - Allows new databases to be created, not permitted by default.
|
||||
* ENV: `DB_READONLY={1|true|yes}` - Makes databases readonly, they are read-write by default.
|
||||
* ENV: `DB_ALLOW_LIST=DB1[,DB2]*` - Restrict what databases can be connected to.
|
||||
* ENV: `DB_PRIVATECACHE` - Keep caches between connections private. Shared by default.
|
||||
* ENV: `DEBUGGING_TAGS` - Outputs xml style tags in query comments for deugging purposes.
|
||||
|
||||
## Agent usage
|
||||
|
||||
The agent is configured as per the configuration schema.
|
||||
|
||||
The only required field is `db` which specifies a local sqlite database to use.
|
||||
|
||||
The schema is exposed via introspection, but you can limit which tables are referenced by
|
||||
|
||||
* Explicitly enumerating them via the `tables` field, or
|
||||
* Toggling the `include_sqlite_meta_tables` to include or exclude sqlite meta tables.
|
||||
|
||||
|
||||
## Docker Build & Run
|
||||
|
||||
```
|
||||
> docker build . -t dc-sqlite-agent:latest
|
||||
> docker run -it --rm -p 8100:8100 dc-sqlite-agent:latest
|
||||
```
|
||||
|
||||
You will want to mount a volume with your database(s) so that they can be referenced in configuration.
|
||||
|
||||
## Dataset
|
||||
|
||||
The dataset used for testing the reference agent is sourced from:
|
||||
|
||||
* https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql
|
||||
|
||||
## Testing Changes to the Agent
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
cabal run graphql-engine:test:tests-dc-api -- test --agent-base-url http://localhost:8100 --agent-config '{"db": "db.chinook2.sqlite"}'
|
||||
```
|
||||
|
||||
From the HGE repo.
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] Pull reference types from a package rather than checked-in files
|
||||
* [x] Health Check
|
||||
* [x] DB Specific Health Checks
|
||||
* [x] Schema
|
||||
* [x] Capabilities
|
||||
* [x] Query
|
||||
* [x] Array Relationships
|
||||
* [x] Object Relationships
|
||||
* [x] Ensure everything is escaped correctly - https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-method-escape
|
||||
* [ ] Or... Use parameterized queries if possible - https://sequelize.org/docs/v6/core-concepts/raw-queries/#bind-parameter
|
||||
* [x] Run test-suite from SDK
|
||||
* [x] Remove old queries module
|
||||
* [x] Relationships / Joins
|
||||
* [x] Rename `resultTT` and other badly named types in the `schema.ts` module
|
||||
* [x] Add ENV Variable for restriction on what databases can be used
|
||||
* [x] Update to the latest types
|
||||
* [x] Port back to hge codebase as an official reference agent
|
||||
* [x] Make escapeSQL global to the query module
|
||||
* [x] Make CORS permissions configurable
|
||||
* [x] Optional DB Allowlist
|
||||
* [x] Fix SDK Test suite to be more flexible about descriptions
|
||||
* [x] READONLY option
|
||||
* [x] CREATE option
|
||||
* [x] Don't create DB option
|
||||
* [x] Aggregate queries
|
||||
* [x] Verbosity settings
|
||||
* [x] Cache settings
|
||||
* [x] Missing WHERE clause from object relationships
|
||||
* [x] Reuse `find_table_relationship` in more scenarios
|
||||
* [x] ORDER clause in aggregates breaks SQLite parser for some reason
|
||||
* [x] Check that looped exist check doesn't cause name conflicts
|
||||
* [ ] `NOT EXISTS IS NULL` != `EXISTS IS NOT NULL`, Example:
|
||||
sqlite> create table test(testid string);
|
||||
sqlite> .schema
|
||||
CREATE TABLE test(testid string);
|
||||
sqlite> select 1 where exists(select * from test where testid is null);
|
||||
sqlite> select 1 where exists(select * from test where testid is not null);
|
||||
sqlite> select 1 where not exists(select * from test where testid is null);
|
||||
1
|
||||
sqlite> select 1 where not exists(select * from test where testid is not null);
|
||||
1
|
||||
sqlite> insert into test(testid) values('foo');
|
||||
sqlite> insert into test(testid) values(NULL);
|
||||
sqlite> select * from test;
|
||||
foo
|
||||
|
||||
sqlite> select 1 where exists(select * from test where testid is null);
|
||||
1
|
||||
sqlite> select 1 where exists(select * from test where testid is not null);
|
||||
1
|
||||
sqlite> select 1 where not exists(select * from test where testid is null);
|
||||
sqlite> select 1 where exists(select * from test where testid is not null);
|
||||
1
|
||||
|
||||
# Known Bugs
|
||||
|
||||
## Tricky Aggregates may have logic bug
|
||||
|
||||
Replicate by running the agent test-suite.
|
3832
dc-agents/sqlite/package-lock.json
generated
Normal file
3832
dc-agents/sqlite/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
dc-agents/sqlite/package.json
Normal file
43
dc-agents/sqlite/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "dc-agent-reference",
|
||||
"version": "1.0.0",
|
||||
"description": "Reference implementation of a Data Connector Agent for Hasura GraphQL Engine",
|
||||
"author": "Hasura (https://github.com/hasura/graphql-engine)",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/hasura/graphql-engine.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/hasura/graphql-engine/issues"
|
||||
},
|
||||
"homepage": "https://github.com/hasura/graphql-engine#readme",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"start": "ts-node ./src/index.ts",
|
||||
"start-no-typecheck": "ts-node --transpileOnly ./src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^7.0.0",
|
||||
"fastify": "^3.29.0",
|
||||
"openapi3-ts": "^2.0.2",
|
||||
"pino-pretty": "^8.0.0",
|
||||
"sequelize": "^6.21.2",
|
||||
"sqlite": "^4.1.1",
|
||||
"sqlite-parser": "^1.0.1",
|
||||
"sqlite3": "^5.0.8",
|
||||
"sqlstring-sqlite": "^0.1.1",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"@types/node": "^16.11.38",
|
||||
"@types/sqlite3": "^3.1.8",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
17
dc-agents/sqlite/src/capabilities.ts
Normal file
17
dc-agents/sqlite/src/capabilities.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { ConfigSchemaResponse, configSchema } from "./config"
|
||||
|
||||
export type Relationships = {}
|
||||
|
||||
export type Capabilities = {
|
||||
relationships: Relationships
|
||||
}
|
||||
|
||||
export type CapabilitiesResponse = {
|
||||
capabilities: Capabilities,
|
||||
configSchemas: ConfigSchemaResponse,
|
||||
}
|
||||
|
||||
export const capabilitiesResponse: CapabilitiesResponse = {
|
||||
capabilities: { relationships: {}},
|
||||
configSchemas: configSchema
|
||||
}
|
60
dc-agents/sqlite/src/config.ts
Normal file
60
dc-agents/sqlite/src/config.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { FastifyRequest } from "fastify"
|
||||
import { SchemaObject } from "openapi3-ts"
|
||||
|
||||
export type Config = {
|
||||
db: string,
|
||||
tables: String[] | null,
|
||||
meta: Boolean
|
||||
}
|
||||
|
||||
export type ConfigSchemaResponse = {
|
||||
configSchema: SchemaObject,
|
||||
otherSchemas: { [schemaName: string]: SchemaObject },
|
||||
}
|
||||
|
||||
export const getConfig = (request: FastifyRequest): Config => {
|
||||
const configHeader = request.headers["x-hasura-dataconnector-config"];
|
||||
const rawConfigJson = Array.isArray(configHeader) ? configHeader[0] : configHeader ?? "{}";
|
||||
const config = JSON.parse(rawConfigJson);
|
||||
return {
|
||||
db: config.db,
|
||||
tables: config.tables ?? null,
|
||||
meta: config.include_sqlite_meta_tables ?? false
|
||||
}
|
||||
}
|
||||
|
||||
export const configSchema: ConfigSchemaResponse = {
|
||||
configSchema: {
|
||||
type: "object",
|
||||
nullable: false,
|
||||
properties: {
|
||||
db: {
|
||||
description: "The SQLite database file to use.",
|
||||
type: "string"
|
||||
},
|
||||
tables: {
|
||||
description: "List of tables to make available in the schema and for querying",
|
||||
type: "array",
|
||||
items: { $ref: "#/otherSchemas/TableName" },
|
||||
nullable: true
|
||||
},
|
||||
include_sqlite_meta_tables: {
|
||||
description: "By default index tables, etc are not included, set this to true to include them.",
|
||||
type: "boolean",
|
||||
nullable: true
|
||||
},
|
||||
DEBUG: {
|
||||
description: "For debugging.",
|
||||
type: "object",
|
||||
additionalProperties: true,
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
},
|
||||
otherSchemas: {
|
||||
TableName: {
|
||||
nullable: false,
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
36
dc-agents/sqlite/src/db.ts
Normal file
36
dc-agents/sqlite/src/db.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Config } from "./config";
|
||||
import { Sequelize } from 'sequelize';
|
||||
import { env } from "process";
|
||||
import { stringToBool } from "./util";
|
||||
import SQLite from 'sqlite3';
|
||||
|
||||
export function connect(config: Config): Sequelize {
|
||||
if(env.DB_ALLOW_LIST != null) {
|
||||
if(!env.DB_ALLOW_LIST.split(',').includes(config.db)) {
|
||||
throw new Error(`Database ${config.db} is not present in DB_ALLOW_LIST 😭`);
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/TryGhost/node-sqlite3/wiki/API#new-sqlite3databasefilename--mode--callback
|
||||
// mode (optional): One or more of
|
||||
// * OPEN_READONLY
|
||||
// * OPEN_READWRITE
|
||||
// * OPEN_CREATE
|
||||
// * OPEN_FULLMUTEX
|
||||
// * OPEN_URI
|
||||
// * OPEN_SHAREDCACHE
|
||||
// * OPEN_PRIVATECACHE
|
||||
// The default value is OPEN_READWRITE | OPEN_CREATE | OPEN_FULLMUTEX.
|
||||
const readMode = stringToBool(process.env['DB_READONLY']) ? SQLite.OPEN_READONLY : SQLite.OPEN_READWRITE;
|
||||
const createMode = stringToBool(process.env['DB_CREATE']) ? SQLite.OPEN_CREATE : 0; // Flag style means 0=off
|
||||
const cacheMode = stringToBool(process.env['DB_PRIVATECACHE']) ? SQLite.OPEN_PRIVATECACHE : SQLite.OPEN_SHAREDCACHE;
|
||||
const mode = readMode | createMode | cacheMode;
|
||||
|
||||
const db = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: config.db,
|
||||
dialectOptions: { mode: mode }
|
||||
});
|
||||
|
||||
return db;
|
||||
};
|
108
dc-agents/sqlite/src/index.ts
Normal file
108
dc-agents/sqlite/src/index.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import Fastify from 'fastify';
|
||||
import FastifyCors from '@fastify/cors';
|
||||
import { getSchema } from './schema';
|
||||
import { queryData } from './query';
|
||||
import { getConfig } from './config';
|
||||
import { CapabilitiesResponse, capabilitiesResponse} from './capabilities';
|
||||
import { connect } from './db';
|
||||
import { stringToBool } from './util';
|
||||
import { QueryResponse, SchemaResponse, QueryRequest } from './types';
|
||||
import * as fs from 'fs'
|
||||
|
||||
const port = Number(process.env.PORT) || 8100;
|
||||
const server = Fastify({ logger: { prettyPrint: true } });
|
||||
|
||||
if(stringToBool(process.env['PERMISSIVE_CORS'])) {
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||
server.register(FastifyCors, {
|
||||
origin: true,
|
||||
methods: ["GET", "POST", "OPTIONS"],
|
||||
allowedHeaders: ["X-Hasura-DataConnector-Config", "X-Hasura-DataConnector-SourceName"]
|
||||
});
|
||||
}
|
||||
|
||||
server.get<{ Reply: CapabilitiesResponse }>("/capabilities", async (request, _response) => {
|
||||
server.log.info({ headers: request.headers, query: request.body, }, "capabilities.request");
|
||||
return capabilitiesResponse;
|
||||
});
|
||||
|
||||
server.get<{ Reply: SchemaResponse }>("/schema", async (request, _response) => {
|
||||
server.log.info({ headers: request.headers, query: request.body, }, "schema.request");
|
||||
const config = getConfig(request);
|
||||
return getSchema(config);
|
||||
});
|
||||
|
||||
server.post<{ Body: QueryRequest, Reply: QueryResponse }>("/query", async (request, _response) => {
|
||||
server.log.info({ headers: request.headers, query: request.body, }, "query.request");
|
||||
const config = getConfig(request);
|
||||
return queryData(config, request.body);
|
||||
});
|
||||
|
||||
server.get("/health", async (request, response) => {
|
||||
const config = getConfig(request);
|
||||
response.type('application/json');
|
||||
|
||||
if(config.db == null) {
|
||||
server.log.info({ headers: request.headers, query: request.body, }, "health.request");
|
||||
// response.statusCode = 204;
|
||||
return { "status": "ok" };
|
||||
} else {
|
||||
server.log.info({ headers: request.headers, query: request.body, }, "health.db.request");
|
||||
const db = connect(config);
|
||||
const [r, m] = await db.query('select 1 where 1 = 1');
|
||||
if(r && JSON.stringify(r) == '[{"1":1}]') {
|
||||
response.statusCode = 204;
|
||||
return { "status": "ok" };
|
||||
} else {
|
||||
response.statusCode = 500;
|
||||
return { "error": "problem executing query", "query_result": r };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.get("/swagger.json", async (request, response) => {
|
||||
fs.readFile('src/types/agent.openapi.json', (err, fileBuffer) => {
|
||||
response.type('application/json');
|
||||
response.send(err || fileBuffer)
|
||||
})
|
||||
})
|
||||
|
||||
server.get("/", async (request, response) => {
|
||||
response.type('text/html');
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Hasura Data Connectors SQLite Agent</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hasura Data Connectors SQLite Agent</h1>
|
||||
<p>See <a href="https://github.com/hasura/graphql-engine#hasura-graphql-engine">
|
||||
the GraphQL Engine repository</a> for more information.</p>
|
||||
<ul>
|
||||
<li><a href="/">GET / - This Page</a>
|
||||
<li><a href="/capabilities">GET /capabilities - Capabilities Metadata</a>
|
||||
<li><a href="/schema">GET /schema - Agent Schema</a>
|
||||
<li><a href="/query">POST /query - Query Handler</a>
|
||||
<li><a href="/health">GET /health - Healthcheck</a>
|
||||
<li><a href="/swagger.json">GET /swagger.json - Swagger JSON</a>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
})
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
server.log.info("interrupted");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
const start = async () => {
|
||||
try {
|
||||
await server.listen(port, "0.0.0.0");
|
||||
}
|
||||
catch (err) {
|
||||
server.log.fatal(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
start();
|
487
dc-agents/sqlite/src/query.ts
Normal file
487
dc-agents/sqlite/src/query.ts
Normal file
@ -0,0 +1,487 @@
|
||||
import { Config } from "./config";
|
||||
import { connect } from "./db";
|
||||
import { coerceUndefinedOrNullToEmptyArray, coerceUndefinedToNull, omap, last, coerceUndefinedOrNullToEmptyRecord, stringToBool, logDeep, isEmptyObject, tableNameEquals } from "./util";
|
||||
import {
|
||||
Expression,
|
||||
BinaryComparisonOperator,
|
||||
ComparisonValue,
|
||||
QueryRequest,
|
||||
ComparisonColumn,
|
||||
TableRelationships,
|
||||
Relationship,
|
||||
RelationshipField,
|
||||
BinaryArrayComparisonOperator,
|
||||
OrderBy,
|
||||
QueryResponse,
|
||||
Field,
|
||||
Aggregate,
|
||||
TableName,
|
||||
} from "./types";
|
||||
|
||||
const SqlString = require('sqlstring-sqlite');
|
||||
|
||||
/** Helper type for convenience. Uses the sqlstring-sqlite library, but should ideally use the function in sequalize.
|
||||
*/
|
||||
type Fields = Record<string, Field>
|
||||
type Aggregates = Record<string, Aggregate>
|
||||
|
||||
function escapeString(x: any): string {
|
||||
return SqlString.escape(x);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier: Unescaped name. E.g. 'Alb"um'
|
||||
* @returns Escaped name. E.g. '"Alb\"um"'
|
||||
*/
|
||||
function escapeIdentifier(identifier: string): string {
|
||||
// TODO: Review this function since the current implementation is off the cuff.
|
||||
const result = identifier.replace(/\\/g,"\\\\").replace(/"/g,'\\"');
|
||||
return `"${result}"`;
|
||||
}
|
||||
|
||||
function extractRawTableName(tableName: TableName): string {
|
||||
if (tableName.length === 1)
|
||||
return tableName[0];
|
||||
else
|
||||
throw new Error(`${tableName.join(".")} is not a valid table`);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tableName: Unescaped table name. E.g. 'Alb"um'
|
||||
* @returns Escaped table name. E.g. '"Alb\"um"'
|
||||
*/
|
||||
function escapeTableName(tableName: TableName): string {
|
||||
return escapeIdentifier(extractRawTableName(tableName));
|
||||
}
|
||||
|
||||
function json_object(ts: Array<TableRelationships>, fields: Fields, table: TableName): string {
|
||||
const result = omap(fields, (k,v) => {
|
||||
switch(v.type) {
|
||||
case "column":
|
||||
return [`${escapeString(k)}, ${escapeIdentifier(v.column)}`];
|
||||
case "relationship":
|
||||
const result = ts.flatMap((x) => {
|
||||
if(tableNameEquals(x.source_table)(table)) {
|
||||
const rel = x.relationships[v.relationship];
|
||||
if(rel) {
|
||||
return [`'${k}', ${relationship(ts, rel, v, table)}`];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
if(result.length < 1) {
|
||||
console.log("Couldn't find relationship for field", k, v, ts);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}).flatMap((e) => e).join(", ");
|
||||
|
||||
return tag('json_object', `JSON_OBJECT(${result})`);
|
||||
}
|
||||
|
||||
function where_clause(ts: Array<TableRelationships>, w: Expression | null, t: TableName): Array<string> {
|
||||
if(w === null) {
|
||||
return [];
|
||||
} else {
|
||||
switch(w.type) {
|
||||
case "not":
|
||||
const aNot = where_clause(ts, w.expression, t);
|
||||
if(aNot.length > 0) {
|
||||
return [`(NOT ${aNot})`];
|
||||
}
|
||||
break;
|
||||
case "and":
|
||||
const aAnd = w.expressions.flatMap(x => where_clause(ts, x, t));
|
||||
if(aAnd.length > 0) {
|
||||
return [`(${aAnd.join(" AND ")})`];
|
||||
}
|
||||
break;
|
||||
case "or":
|
||||
const aOr = w.expressions.flatMap(x => where_clause(ts, x, t));
|
||||
if(aOr.length > 0) {
|
||||
return [`(${aOr.join(" OR ")})`];
|
||||
}
|
||||
break;
|
||||
case "unary_op":
|
||||
switch(w.operator) {
|
||||
case 'is_null':
|
||||
if(w.column.path.length < 1) {
|
||||
return [`(${escapeIdentifier(w.column.name)} IS NULL)`];
|
||||
} else {
|
||||
return [exists(ts, w.column, t, 'IS NULL')];
|
||||
}
|
||||
default:
|
||||
if(w.column.path.length < 1) {
|
||||
return [`(${escapeIdentifier(w.column.name)} ${w.operator})`];
|
||||
} else {
|
||||
return [exists(ts, w.column, t, w.operator)];
|
||||
}
|
||||
}
|
||||
case "binary_op":
|
||||
const bop = bop_op(w.operator);
|
||||
if(w.column.path.length < 1) {
|
||||
return [`${escapeIdentifier(w.column.name)} ${bop} ${bop_val(w.value, t)}`];
|
||||
} else {
|
||||
return [exists(ts, w.column, t, `${bop} ${bop_val(w.value, t)}`)];
|
||||
}
|
||||
case "binary_arr_op":
|
||||
const bopA = bop_array(w.operator);
|
||||
if(w.column.path.length < 1) {
|
||||
return [`(${escapeIdentifier(w.column.name)} ${bopA} (${w.values.map(v => escapeString(v)).join(", ")}))`];
|
||||
} else {
|
||||
return [exists(ts,w.column,t, `${bopA} (${w.values.map(v => escapeString(v)).join(", ")})`)];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function exists(ts: Array<TableRelationships>, c: ComparisonColumn, t: TableName, o: string): string {
|
||||
// NOTE: An N suffix doesn't guarantee that conflicts are avoided.
|
||||
const r = join_path(ts, t, c.path, 0);
|
||||
const f = `FROM ${r.f.map(x => `${x.from} AS ${x.as}`).join(', ')}`;
|
||||
return tag('exists',`EXISTS (SELECT 1 ${f} WHERE ${[...r.j, `${last(r.f).as}.${escapeIdentifier(c.name)} ${o}`].join(' AND ')})`);
|
||||
}
|
||||
|
||||
/** Develops a from clause for an operation with a path - a relationship referenced column
|
||||
*
|
||||
* Artist [Albums] Title
|
||||
* FROM Album Album_PATH_XX ...
|
||||
* WHERE Album_PATH_XX.ArtistId = Artist.ArtistId
|
||||
* Album_PATH_XX.Title IS NULL
|
||||
*
|
||||
* @param ts
|
||||
* @param table
|
||||
* @param path
|
||||
* @returns the from clause for the EXISTS query
|
||||
*/
|
||||
function join_path(ts: TableRelationships[], table: TableName, path: Array<string>, level: number): {f: Array<{from: string, as: string}>, j: string[]} {
|
||||
const r = find_table_relationship(ts, table);
|
||||
if(path.length < 1) {
|
||||
return {f: [], j: []};
|
||||
} else if(r === null) {
|
||||
throw new Error(`Couldn't find relationship ${ts}, ${table.join(".")} - This shouldn't happen.`);
|
||||
} else {
|
||||
const x = r.relationships[path[0]];
|
||||
const n = join_path(ts, x.target_table, path.slice(1), level+1);
|
||||
const m =
|
||||
omap(
|
||||
x.column_mapping,
|
||||
(sourceColumnName,targetColumnName) =>
|
||||
`${depthQualifyIdentifier(level-1,extractRawTableName(table))}.${escapeIdentifier(sourceColumnName)} = ${depthQualifyIdentifier(level, extractRawTableName(x.target_table))}.${escapeIdentifier(targetColumnName)}`
|
||||
)
|
||||
.join(' AND ');
|
||||
return {f: [{from: escapeTableName(x.target_table), as: depthQualifyIdentifier(level, extractRawTableName(x.target_table))}, ...n.f], j: [m, ...n.j]};
|
||||
}
|
||||
}
|
||||
|
||||
function depthQualifyIdentifier(depth: number, identifier:string): string {
|
||||
if(depth < 0) {
|
||||
return escapeIdentifier(identifier);
|
||||
} else {
|
||||
return escapeIdentifier(`${identifier}_${depth}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ts Array of Table Relationships
|
||||
* @param t Table Name
|
||||
* @returns Relationships matching table-name
|
||||
*/
|
||||
function find_table_relationship(ts: Array<TableRelationships>, t: TableName): TableRelationships | null {
|
||||
for(var i = 0; i < ts.length; i++) {
|
||||
const r = ts[i];
|
||||
if(tableNameEquals(r.source_table)(t)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function cast_aggregate_function(f: string): string {
|
||||
switch(f) {
|
||||
case 'avg':
|
||||
case 'max':
|
||||
case 'min':
|
||||
case 'sum':
|
||||
case 'total':
|
||||
return f;
|
||||
default:
|
||||
throw new Error(`Aggregate function ${f} is not supported by SQLite. See: https://www.sqlite.org/lang_aggfunc.html`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an Aggregate query expression.
|
||||
*
|
||||
* NOTE: ORDER Clauses are currently broken due to SQLite parser issue.
|
||||
*
|
||||
* @param table
|
||||
* @param aggregates
|
||||
* @param innerFromClauses
|
||||
* @returns
|
||||
*/
|
||||
function aggregates_query(
|
||||
table: TableName,
|
||||
aggregates: Aggregates,
|
||||
innerFromClauses: string,
|
||||
): Array<string> {
|
||||
if(isEmptyObject(aggregates)) {
|
||||
return [];
|
||||
} else {
|
||||
const aggregate_pairs = omap(aggregates, (k,v) => {
|
||||
switch(v.type) {
|
||||
case 'star_count':
|
||||
return `${escapeString(k)}, COUNT(*)`;
|
||||
case 'column_count':
|
||||
if(v.distinct) {
|
||||
return `${escapeString(k)}, COUNT(DISTINCT ${escapeIdentifier(v.column)})`;
|
||||
} else {
|
||||
return `${escapeString(k)}, COUNT(${escapeIdentifier(v.column)})`;
|
||||
}
|
||||
case 'single_column':
|
||||
return `${escapeString(k)}, ${cast_aggregate_function(v.function)}(${escapeIdentifier(v.column)})`;
|
||||
}
|
||||
}).join(', ');
|
||||
|
||||
return [`'aggregates', (SELECT JSON_OBJECT(${aggregate_pairs}) FROM (SELECT * from ${escapeTableName(table)} ${innerFromClauses}))`]
|
||||
}
|
||||
}
|
||||
|
||||
function array_relationship(
|
||||
ts: Array<TableRelationships>,
|
||||
table: TableName,
|
||||
wJoin: Array<string>,
|
||||
fields: Fields,
|
||||
aggregates: Aggregates,
|
||||
wWhere: Expression | null,
|
||||
wLimit: number | null,
|
||||
wOffset: number | null,
|
||||
wOrder: Array<OrderBy>,
|
||||
): string {
|
||||
const innerFromClauses = `${where(ts, wWhere, wJoin, table)} ${order(wOrder)} ${limit(wLimit)} ${offset(wOffset)}`;
|
||||
const aggregateSelect = aggregates_query(table, aggregates, innerFromClauses);
|
||||
const fieldSelect = isEmptyObject(fields) ? [] : [`'rows', JSON_GROUP_ARRAY(j)`];
|
||||
const fieldFrom = isEmptyObject(fields) ? '' : (() => {
|
||||
// NOTE: The order of table prefixes are currently assumed to be from "parent" to "child".
|
||||
// NOTE: The reuse of the 'j' identifier should be safe due to scoping. This is confirmed in testing.
|
||||
if(wOrder.length < 1) {
|
||||
return `FROM ( SELECT ${json_object(ts, fields, table)} AS j FROM ${escapeTableName(table)} ${innerFromClauses})`;
|
||||
} else {
|
||||
const innerSelect = `SELECT * FROM ${escapeTableName(table)} ${innerFromClauses}`;
|
||||
return `FROM (SELECT ${json_object(ts, fields, table)} AS j FROM (${innerSelect}) AS ${table})`;
|
||||
}
|
||||
})()
|
||||
|
||||
return tag('array_relationship',`(SELECT JSON_OBJECT(${[...fieldSelect, ...aggregateSelect].join(', ')}) ${fieldFrom})`);
|
||||
}
|
||||
|
||||
function object_relationship(
|
||||
ts: Array<TableRelationships>,
|
||||
table: TableName,
|
||||
wJoin: Array<string>,
|
||||
fields: Fields,
|
||||
): string {
|
||||
// NOTE: The order of table prefixes are from "parent" to "child".
|
||||
const innerFrom = `${escapeTableName(table)} ${where(ts, null, wJoin, table)}`;
|
||||
return tag('object_relationship',
|
||||
`(SELECT JSON_OBJECT('rows', JSON_ARRAY(${json_object(ts, fields, table)})) AS j FROM ${innerFrom})`);
|
||||
}
|
||||
|
||||
function relationship(ts: Array<TableRelationships>, r: Relationship, field: RelationshipField, table: TableName): string {
|
||||
const wJoin = omap(
|
||||
r.column_mapping,
|
||||
(k,v) => `${escapeTableName(table)}.${escapeIdentifier(k)} = ${escapeTableName(r.target_table)}.${escapeIdentifier(v)}`
|
||||
);
|
||||
|
||||
switch(r.relationship_type) {
|
||||
case 'object':
|
||||
return tag('relationship', object_relationship(
|
||||
ts,
|
||||
r.target_table,
|
||||
wJoin,
|
||||
coerceUndefinedOrNullToEmptyRecord(field.query.fields),
|
||||
));
|
||||
|
||||
case 'array':
|
||||
return tag('relationship', array_relationship(
|
||||
ts,
|
||||
r.target_table,
|
||||
wJoin,
|
||||
coerceUndefinedOrNullToEmptyRecord(field.query.fields),
|
||||
coerceUndefinedOrNullToEmptyRecord(field.query.aggregates),
|
||||
coerceUndefinedToNull(field.query.where),
|
||||
coerceUndefinedToNull(field.query.limit),
|
||||
coerceUndefinedToNull(field.query.offset),
|
||||
coerceUndefinedOrNullToEmptyArray(field.query.order_by),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: There is a bug in this implementation where vals can reference columns with paths.
|
||||
function bop_col(c: ComparisonColumn, t: TableName): string {
|
||||
if(c.path.length < 1) {
|
||||
return tag('bop_col', `${escapeTableName(t)}.${escapeIdentifier(c.name)}`);
|
||||
} else {
|
||||
throw new Error(`bop_col shouldn't be handling paths.`);
|
||||
}
|
||||
}
|
||||
|
||||
function bop_array(o: BinaryArrayComparisonOperator): string {
|
||||
switch(o) {
|
||||
case 'in': return tag('bop_array','IN');
|
||||
default: return tag('bop_array', o);
|
||||
}
|
||||
}
|
||||
|
||||
function bop_op(o: BinaryComparisonOperator): string {
|
||||
let result = o;
|
||||
switch(o) {
|
||||
case 'equal': result = "="; break;
|
||||
case 'greater_than': result = ">"; break;
|
||||
case 'greater_than_or_equal': result = ">="; break;
|
||||
case 'less_than': result = "<"; break;
|
||||
case 'less_than_or_equal': result = "<="; break;
|
||||
}
|
||||
return tag('bop_op',result);
|
||||
}
|
||||
|
||||
function bop_val(v: ComparisonValue, t: TableName): string {
|
||||
switch(v.type) {
|
||||
case "column": return tag('bop_val', bop_col(v.column, t));
|
||||
case "scalar": return tag('bop_val', escapeString(v.value));
|
||||
}
|
||||
}
|
||||
|
||||
function order(o: Array<OrderBy>): string {
|
||||
if(o.length < 1) {
|
||||
return "";
|
||||
}
|
||||
const result = o.map(e => `${e.column} ${e.ordering}`).join(', ');
|
||||
return tag('order',`ORDER BY ${result}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whereExpression Nested expression used in the associated where clause
|
||||
* @param joinArray Join clauses
|
||||
* @returns string representing the combined where clause
|
||||
*/
|
||||
function where(ts: Array<TableRelationships>, whereExpression: Expression | null, joinArray: Array<string>, t: TableName): string {
|
||||
const clauses = [...where_clause(ts, whereExpression, t), ...joinArray];
|
||||
if(clauses.length < 1) {
|
||||
return "";
|
||||
} else {
|
||||
return tag('where',`WHERE ${clauses.join(" AND ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function limit(l: number | null): string {
|
||||
if(l === null) {
|
||||
return "";
|
||||
} else {
|
||||
return tag('limit',`LIMIT ${l}`);
|
||||
}
|
||||
}
|
||||
|
||||
function offset(o: number | null): string {
|
||||
if(o === null) {
|
||||
return "";
|
||||
} else {
|
||||
return tag('offset', `OFFSET ${o}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Top-Level Query Function.
|
||||
*/
|
||||
function query(request: QueryRequest): string {
|
||||
const result = array_relationship(
|
||||
request.table_relationships,
|
||||
request.table,
|
||||
[],
|
||||
coerceUndefinedOrNullToEmptyRecord(request.query.fields),
|
||||
coerceUndefinedOrNullToEmptyRecord(request.query.aggregates),
|
||||
coerceUndefinedToNull(request.query.where),
|
||||
coerceUndefinedToNull(request.query.limit),
|
||||
coerceUndefinedToNull(request.query.offset),
|
||||
coerceUndefinedOrNullToEmptyArray(request.query.order_by),
|
||||
);
|
||||
return tag('query', `SELECT ${result} as data`);
|
||||
}
|
||||
|
||||
/** Format the DB response into a /query response.
|
||||
*
|
||||
* Note: There should always be one result since 0 rows still generates an empty JSON array.
|
||||
*/
|
||||
function output(rows: any): QueryResponse {
|
||||
return JSON.parse(rows[0].data);
|
||||
}
|
||||
|
||||
const DEBUGGING_TAGS = stringToBool(process.env['DEBUGGING_TAGS']);
|
||||
/** Function to add SQL comments to the generated SQL to tag which procedures generated what text.
|
||||
*
|
||||
* comment('a','b') => '/*\<a>\*\/ b /*\</a>*\/'
|
||||
*/
|
||||
function tag(t: string, s: string): string {
|
||||
if(DEBUGGING_TAGS) {
|
||||
return `/*<${t}>*/ ${s} /*</${t}>*/`;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs a query and returns results
|
||||
*
|
||||
* Limitations:
|
||||
*
|
||||
* - Binary Array Operations not currently supported.
|
||||
*
|
||||
* The current algorithm is to first create a query, then execute it, returning results.
|
||||
*
|
||||
* Method for adding relationship fields:
|
||||
*
|
||||
* - JSON aggregation similar to Postgres' approach.
|
||||
* - 4.13. The json_group_array() and json_group_object() aggregate SQL functions
|
||||
* - https://www.sqlite.org/json1.html#jgrouparray
|
||||
*
|
||||
|
||||
|
||||
* Example of a test query:
|
||||
*
|
||||
* ```
|
||||
* query MyQuery {
|
||||
* Artist(limit: 5, order_by: {ArtistId: asc}, where: {Name: {_neq: "Accept"}, _and: {Name: {_is_null: false}}}, offset: 3) {
|
||||
* ArtistId
|
||||
* Name
|
||||
* Albums(where: {Title: {_is_null: false, _gt: "A", _nin: "foo"}}, limit: 2) {
|
||||
* AlbumId
|
||||
* Title
|
||||
* ArtistId
|
||||
* Tracks(limit: 1) {
|
||||
* Name
|
||||
* TrackId
|
||||
* }
|
||||
* Artist {
|
||||
* ArtistId
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* Track(limit: 3) {
|
||||
* Name
|
||||
* Album {
|
||||
* Title
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export async function queryData(config: Config, queryRequest: QueryRequest): Promise<QueryResponse> {
|
||||
const db = connect(config); // TODO: Should this be cached?
|
||||
const q = query(queryRequest);
|
||||
const [result, metadata] = await db.query(q);
|
||||
|
||||
return output(result);
|
||||
}
|
161
dc-agents/sqlite/src/schema.ts
Normal file
161
dc-agents/sqlite/src/schema.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import { SchemaResponse, ScalarType, ColumnInfo, TableInfo } from "./types"
|
||||
import { Config } from "./config";
|
||||
import { connect } from './db';
|
||||
|
||||
var sqliteParser = require('sqlite-parser');
|
||||
|
||||
type TableInfoInternal = {
|
||||
name: string,
|
||||
type: string,
|
||||
tbl_name: string,
|
||||
rootpage: Number,
|
||||
sql: string
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ColumnInfoInternalype as per HGE DataConnector IR
|
||||
* @returns SQLite's corresponding column type
|
||||
*
|
||||
* Note: This defaults to "string" when a type is not anticipated
|
||||
* in order to be as permissive as possible but logs when
|
||||
* this happens.
|
||||
*/
|
||||
function columnCast(ColumnInfoInternalype: string): ScalarType {
|
||||
switch(ColumnInfoInternalype) {
|
||||
case "string":
|
||||
case "number":
|
||||
case "bool": return ColumnInfoInternalype as ScalarType;
|
||||
case "boolean": return "bool";
|
||||
case "numeric": return "number";
|
||||
case "integer": return "number";
|
||||
case "double": return "number";
|
||||
case "float": return "number";
|
||||
case "text": return "string";
|
||||
default:
|
||||
console.log(`Unknown SQLite column type: ${ColumnInfoInternalype}. Interpreting as string.`)
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
|
||||
function getColumns(ast : Array<any>) : Array<ColumnInfo> {
|
||||
return ast.map(c => {
|
||||
return ({
|
||||
name: c.name,
|
||||
type: columnCast(datatypeCast(c.datatype)),
|
||||
nullable: nullableCast(c.definition)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Interpret the sqlite-parser datatype as a schema column response type.
|
||||
function datatypeCast(d: any): any {
|
||||
switch(d.variant) {
|
||||
case "datetime": return 'string';
|
||||
default: return d.affinity;
|
||||
}
|
||||
}
|
||||
|
||||
function nullableCast(ds: Array<any>): boolean {
|
||||
for(var d of ds) {
|
||||
if(d.type === 'constraint' && d.variant == 'not null') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function formatTableInfo(info : TableInfoInternal): TableInfo {
|
||||
const ast = sqliteParser(info.sql);
|
||||
const ddl = ddlColumns(ast);
|
||||
const pks = ddlPKs(ast);
|
||||
const pk = pks.length > 0 ? { primary_key: pks } : {};
|
||||
|
||||
// TODO: Should we include something for the description here?
|
||||
return {
|
||||
name: [info.name],
|
||||
...pk,
|
||||
description: info.sql,
|
||||
columns: getColumns(ddl)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param table
|
||||
* @returns true if the table is an SQLite meta table such as a sequence, index, etc.
|
||||
*/
|
||||
function isMeta(table : TableInfoInternal) {
|
||||
return table.type != 'table';
|
||||
}
|
||||
|
||||
function includeTable(config: Config, table: TableInfoInternal): boolean {
|
||||
if(config.tables === null) {
|
||||
if(isMeta(table) && ! config.meta) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return config.tables.indexOf(table.name) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls columns from the output of sqlite-parser.
|
||||
* Note that this doesn't check if duplicates are present and will emit them as many times as they are present.
|
||||
* This is done as an easy way to preserve order.
|
||||
*
|
||||
* @param ddl - The output of sqlite-parser
|
||||
* @returns - List of columns as present in the output of sqlite-parser.
|
||||
*/
|
||||
function ddlColumns(ddl: any): Array<any> {
|
||||
if(ddl.type != 'statement' || ddl.variant != 'list') {
|
||||
throw new Error("Encountered a non-statement or non-list when parsing DDL for table.");
|
||||
}
|
||||
return ddl.statement.flatMap((t: any) => {
|
||||
if(t.type != 'statement' || t.variant != 'create' || t.format != 'table') {
|
||||
return [];
|
||||
}
|
||||
return t.definition.flatMap((c: any) => {
|
||||
if(c.type != 'definition' || c.variant != 'column') {
|
||||
return [];
|
||||
}
|
||||
return [c];
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function ddlPKs(ddl: any): Array<any> {
|
||||
if(ddl.type != 'statement' || ddl.variant != 'list') {
|
||||
throw new Error("Encountered a non-statement or non-list when parsing DDL for table.");
|
||||
}
|
||||
return ddl.statement.flatMap((t: any) => {
|
||||
if(t.type != 'statement' || t.variant != 'create' || t.format != 'table') {
|
||||
return [];
|
||||
}
|
||||
return t.definition.flatMap((c: any) => {
|
||||
if(c.type != 'definition' || c.variant != 'constraint'
|
||||
|| c.definition.length != 1 || c.definition[0].type != 'constraint' || c.definition[0].variant != 'primary key') {
|
||||
return [];
|
||||
}
|
||||
return c.columns.flatMap((x:any) => {
|
||||
if(x.type == 'identifier' && x.variant == 'column') {
|
||||
return [x.name];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export async function getSchema(config: Config): Promise<SchemaResponse> {
|
||||
const db = connect(config);
|
||||
const [results, metadata] = await db.query("SELECT * from sqlite_schema");
|
||||
const resultsT: Array<TableInfoInternal> = results as unknown as Array<TableInfoInternal>;
|
||||
const filtered: Array<TableInfoInternal> = resultsT.filter(table => includeTable(config,table));
|
||||
const result: Array<TableInfo> = filtered.map(formatTableInfo);
|
||||
|
||||
return {
|
||||
tables: result
|
||||
};
|
||||
};
|
1242
dc-agents/sqlite/src/types/agent.openapi.json
Normal file
1242
dc-agents/sqlite/src/types/agent.openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
57
dc-agents/sqlite/src/types/index.ts
Normal file
57
dc-agents/sqlite/src/types/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type { Aggregate } from './models/Aggregate';
|
||||
export type { AndExpression } from './models/AndExpression';
|
||||
export type { AnotherColumnComparison } from './models/AnotherColumnComparison';
|
||||
export type { ApplyBinaryArrayComparisonOperator } from './models/ApplyBinaryArrayComparisonOperator';
|
||||
export type { ApplyBinaryComparisonOperator } from './models/ApplyBinaryComparisonOperator';
|
||||
export type { ApplyUnaryComparisonOperator } from './models/ApplyUnaryComparisonOperator';
|
||||
export type { BinaryArrayComparisonOperator } from './models/BinaryArrayComparisonOperator';
|
||||
export type { BinaryComparisonOperator } from './models/BinaryComparisonOperator';
|
||||
export type { BooleanOperators } from './models/BooleanOperators';
|
||||
export type { Capabilities } from './models/Capabilities';
|
||||
export type { CapabilitiesResponse } from './models/CapabilitiesResponse';
|
||||
export type { ColumnCountAggregate } from './models/ColumnCountAggregate';
|
||||
export type { ColumnField } from './models/ColumnField';
|
||||
export type { ColumnFieldValue } from './models/ColumnFieldValue';
|
||||
export type { ColumnInfo } from './models/ColumnInfo';
|
||||
export type { ComparisonColumn } from './models/ComparisonColumn';
|
||||
export type { ComparisonOperators } from './models/ComparisonOperators';
|
||||
export type { ComparisonValue } from './models/ComparisonValue';
|
||||
export type { ConfigSchemaResponse } from './models/ConfigSchemaResponse';
|
||||
export type { Expression } from './models/Expression';
|
||||
export type { Field } from './models/Field';
|
||||
export type { FilteringCapabilities } from './models/FilteringCapabilities';
|
||||
export type { MutationCapabilities } from './models/MutationCapabilities';
|
||||
export type { NotExpression } from './models/NotExpression';
|
||||
export type { NullColumnFieldValue } from './models/NullColumnFieldValue';
|
||||
export type { OpenApiDiscriminator } from './models/OpenApiDiscriminator';
|
||||
export type { OpenApiExternalDocumentation } from './models/OpenApiExternalDocumentation';
|
||||
export type { OpenApiReference } from './models/OpenApiReference';
|
||||
export type { OpenApiSchema } from './models/OpenApiSchema';
|
||||
export type { OpenApiXml } from './models/OpenApiXml';
|
||||
export type { OrderBy } from './models/OrderBy';
|
||||
export type { OrderType } from './models/OrderType';
|
||||
export type { OrExpression } from './models/OrExpression';
|
||||
export type { Query } from './models/Query';
|
||||
export type { QueryCapabilities } from './models/QueryCapabilities';
|
||||
export type { QueryRequest } from './models/QueryRequest';
|
||||
export type { QueryResponse } from './models/QueryResponse';
|
||||
export type { Relationship } from './models/Relationship';
|
||||
export type { RelationshipCapabilities } from './models/RelationshipCapabilities';
|
||||
export type { RelationshipField } from './models/RelationshipField';
|
||||
export type { RelationshipType } from './models/RelationshipType';
|
||||
export type { ScalarType } from './models/ScalarType';
|
||||
export type { ScalarValue } from './models/ScalarValue';
|
||||
export type { ScalarValueComparison } from './models/ScalarValueComparison';
|
||||
export type { SchemaResponse } from './models/SchemaResponse';
|
||||
export type { SingleColumnAggregate } from './models/SingleColumnAggregate';
|
||||
export type { SingleColumnAggregateFunction } from './models/SingleColumnAggregateFunction';
|
||||
export type { StarCountAggregate } from './models/StarCountAggregate';
|
||||
export type { SubscriptionCapabilities } from './models/SubscriptionCapabilities';
|
||||
export type { TableInfo } from './models/TableInfo';
|
||||
export type { TableName } from './models/TableName';
|
||||
export type { TableRelationships } from './models/TableRelationships';
|
||||
export type { UnaryComparisonOperator } from './models/UnaryComparisonOperator';
|
10
dc-agents/sqlite/src/types/models/Aggregate.ts
Normal file
10
dc-agents/sqlite/src/types/models/Aggregate.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ColumnCountAggregate } from './ColumnCountAggregate';
|
||||
import type { SingleColumnAggregate } from './SingleColumnAggregate';
|
||||
import type { StarCountAggregate } from './StarCountAggregate';
|
||||
|
||||
export type Aggregate = (ColumnCountAggregate | SingleColumnAggregate | StarCountAggregate);
|
||||
|
11
dc-agents/sqlite/src/types/models/AndExpression.ts
Normal file
11
dc-agents/sqlite/src/types/models/AndExpression.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Expression } from './Expression';
|
||||
|
||||
export type AndExpression = {
|
||||
expressions: Array<Expression>;
|
||||
type: 'and';
|
||||
};
|
||||
|
11
dc-agents/sqlite/src/types/models/AnotherColumnComparison.ts
Normal file
11
dc-agents/sqlite/src/types/models/AnotherColumnComparison.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ComparisonColumn } from './ComparisonColumn';
|
||||
|
||||
export type AnotherColumnComparison = {
|
||||
column: ComparisonColumn;
|
||||
type: 'column';
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BinaryArrayComparisonOperator } from './BinaryArrayComparisonOperator';
|
||||
import type { ComparisonColumn } from './ComparisonColumn';
|
||||
import type { ScalarValue } from './ScalarValue';
|
||||
|
||||
export type ApplyBinaryArrayComparisonOperator = {
|
||||
column: ComparisonColumn;
|
||||
operator: BinaryArrayComparisonOperator;
|
||||
type: 'binary_arr_op';
|
||||
values: Array<ScalarValue>;
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BinaryComparisonOperator } from './BinaryComparisonOperator';
|
||||
import type { ComparisonColumn } from './ComparisonColumn';
|
||||
import type { ComparisonValue } from './ComparisonValue';
|
||||
|
||||
export type ApplyBinaryComparisonOperator = {
|
||||
column: ComparisonColumn;
|
||||
operator: BinaryComparisonOperator;
|
||||
type: 'binary_op';
|
||||
value: ComparisonValue;
|
||||
};
|
||||
|
@ -0,0 +1,13 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ComparisonColumn } from './ComparisonColumn';
|
||||
import type { UnaryComparisonOperator } from './UnaryComparisonOperator';
|
||||
|
||||
export type ApplyUnaryComparisonOperator = {
|
||||
column: ComparisonColumn;
|
||||
operator: UnaryComparisonOperator;
|
||||
type: 'unary_op';
|
||||
};
|
||||
|
@ -0,0 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type BinaryArrayComparisonOperator = ('in' | string);
|
||||
|
@ -0,0 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type BinaryComparisonOperator = ('less_than' | 'less_than_or_equal' | 'greater_than' | 'greater_than_or_equal' | 'equal' | string);
|
||||
|
7
dc-agents/sqlite/src/types/models/BooleanOperators.ts
Normal file
7
dc-agents/sqlite/src/types/models/BooleanOperators.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type BooleanOperators = {
|
||||
};
|
||||
|
18
dc-agents/sqlite/src/types/models/Capabilities.ts
Normal file
18
dc-agents/sqlite/src/types/models/Capabilities.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { FilteringCapabilities } from './FilteringCapabilities';
|
||||
import type { MutationCapabilities } from './MutationCapabilities';
|
||||
import type { QueryCapabilities } from './QueryCapabilities';
|
||||
import type { RelationshipCapabilities } from './RelationshipCapabilities';
|
||||
import type { SubscriptionCapabilities } from './SubscriptionCapabilities';
|
||||
|
||||
export type Capabilities = {
|
||||
filtering?: FilteringCapabilities;
|
||||
mutations?: MutationCapabilities;
|
||||
queries?: QueryCapabilities;
|
||||
relationships?: RelationshipCapabilities;
|
||||
subscriptions?: SubscriptionCapabilities;
|
||||
};
|
||||
|
12
dc-agents/sqlite/src/types/models/CapabilitiesResponse.ts
Normal file
12
dc-agents/sqlite/src/types/models/CapabilitiesResponse.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Capabilities } from './Capabilities';
|
||||
import type { ConfigSchemaResponse } from './ConfigSchemaResponse';
|
||||
|
||||
export type CapabilitiesResponse = {
|
||||
capabilities: Capabilities;
|
||||
configSchemas: ConfigSchemaResponse;
|
||||
};
|
||||
|
16
dc-agents/sqlite/src/types/models/ColumnCountAggregate.ts
Normal file
16
dc-agents/sqlite/src/types/models/ColumnCountAggregate.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ColumnCountAggregate = {
|
||||
/**
|
||||
* The column to apply the count aggregate function to
|
||||
*/
|
||||
column: string;
|
||||
/**
|
||||
* Whether or not only distinct items should be counted
|
||||
*/
|
||||
distinct: boolean;
|
||||
type: 'column_count';
|
||||
};
|
||||
|
9
dc-agents/sqlite/src/types/models/ColumnField.ts
Normal file
9
dc-agents/sqlite/src/types/models/ColumnField.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ColumnField = {
|
||||
column: string;
|
||||
type: 'column';
|
||||
};
|
||||
|
7
dc-agents/sqlite/src/types/models/ColumnFieldValue.ts
Normal file
7
dc-agents/sqlite/src/types/models/ColumnFieldValue.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ColumnFieldValue = {
|
||||
};
|
||||
|
22
dc-agents/sqlite/src/types/models/ColumnInfo.ts
Normal file
22
dc-agents/sqlite/src/types/models/ColumnInfo.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ScalarType } from './ScalarType';
|
||||
|
||||
export type ColumnInfo = {
|
||||
/**
|
||||
* Column description
|
||||
*/
|
||||
description?: string | null;
|
||||
/**
|
||||
* Column name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Is column nullable
|
||||
*/
|
||||
nullable: boolean;
|
||||
type: ScalarType;
|
||||
};
|
||||
|
15
dc-agents/sqlite/src/types/models/ComparisonColumn.ts
Normal file
15
dc-agents/sqlite/src/types/models/ComparisonColumn.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ComparisonColumn = {
|
||||
/**
|
||||
* The name of the column
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The relationship path from the current query table to the table that contains the specified column. Empty array means the current query table.
|
||||
*/
|
||||
path: Array<string>;
|
||||
};
|
||||
|
7
dc-agents/sqlite/src/types/models/ComparisonOperators.ts
Normal file
7
dc-agents/sqlite/src/types/models/ComparisonOperators.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ComparisonOperators = {
|
||||
};
|
||||
|
9
dc-agents/sqlite/src/types/models/ComparisonValue.ts
Normal file
9
dc-agents/sqlite/src/types/models/ComparisonValue.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AnotherColumnComparison } from './AnotherColumnComparison';
|
||||
import type { ScalarValueComparison } from './ScalarValueComparison';
|
||||
|
||||
export type ComparisonValue = (ScalarValueComparison | AnotherColumnComparison);
|
||||
|
11
dc-agents/sqlite/src/types/models/ConfigSchemaResponse.ts
Normal file
11
dc-agents/sqlite/src/types/models/ConfigSchemaResponse.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { OpenApiSchema } from './OpenApiSchema';
|
||||
|
||||
export type ConfigSchemaResponse = {
|
||||
configSchema: OpenApiSchema;
|
||||
otherSchemas: Record<string, OpenApiSchema>;
|
||||
};
|
||||
|
13
dc-agents/sqlite/src/types/models/Expression.ts
Normal file
13
dc-agents/sqlite/src/types/models/Expression.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AndExpression } from './AndExpression';
|
||||
import type { ApplyBinaryArrayComparisonOperator } from './ApplyBinaryArrayComparisonOperator';
|
||||
import type { ApplyBinaryComparisonOperator } from './ApplyBinaryComparisonOperator';
|
||||
import type { ApplyUnaryComparisonOperator } from './ApplyUnaryComparisonOperator';
|
||||
import type { NotExpression } from './NotExpression';
|
||||
import type { OrExpression } from './OrExpression';
|
||||
|
||||
export type Expression = (ApplyBinaryArrayComparisonOperator | OrExpression | ApplyUnaryComparisonOperator | ApplyBinaryComparisonOperator | NotExpression | AndExpression);
|
||||
|
9
dc-agents/sqlite/src/types/models/Field.ts
Normal file
9
dc-agents/sqlite/src/types/models/Field.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ColumnField } from './ColumnField';
|
||||
import type { RelationshipField } from './RelationshipField';
|
||||
|
||||
export type Field = (RelationshipField | ColumnField);
|
||||
|
12
dc-agents/sqlite/src/types/models/FilteringCapabilities.ts
Normal file
12
dc-agents/sqlite/src/types/models/FilteringCapabilities.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BooleanOperators } from './BooleanOperators';
|
||||
import type { ComparisonOperators } from './ComparisonOperators';
|
||||
|
||||
export type FilteringCapabilities = {
|
||||
booleanOperators: BooleanOperators;
|
||||
comparisonOperators: ComparisonOperators;
|
||||
};
|
||||
|
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type MutationCapabilities = {
|
||||
};
|
||||
|
11
dc-agents/sqlite/src/types/models/NotExpression.ts
Normal file
11
dc-agents/sqlite/src/types/models/NotExpression.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Expression } from './Expression';
|
||||
|
||||
export type NotExpression = {
|
||||
expression: Expression;
|
||||
type: 'not';
|
||||
};
|
||||
|
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type NullColumnFieldValue = null;
|
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type OpenApiDiscriminator = {
|
||||
mapping?: Record<string, string>;
|
||||
propertyName: string;
|
||||
};
|
||||
|
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type OpenApiExternalDocumentation = {
|
||||
description?: string;
|
||||
url: string;
|
||||
};
|
||||
|
8
dc-agents/sqlite/src/types/models/OpenApiReference.ts
Normal file
8
dc-agents/sqlite/src/types/models/OpenApiReference.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type OpenApiReference = {
|
||||
$ref: string;
|
||||
};
|
||||
|
47
dc-agents/sqlite/src/types/models/OpenApiSchema.ts
Normal file
47
dc-agents/sqlite/src/types/models/OpenApiSchema.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { OpenApiDiscriminator } from './OpenApiDiscriminator';
|
||||
import type { OpenApiExternalDocumentation } from './OpenApiExternalDocumentation';
|
||||
import type { OpenApiReference } from './OpenApiReference';
|
||||
import type { OpenApiXml } from './OpenApiXml';
|
||||
|
||||
export type OpenApiSchema = {
|
||||
additionalProperties?: any;
|
||||
allOf?: Array<(OpenApiSchema | OpenApiReference)>;
|
||||
anyOf?: Array<(OpenApiSchema | OpenApiReference)>;
|
||||
default?: any;
|
||||
deprecated?: boolean;
|
||||
description?: string;
|
||||
discriminator?: OpenApiDiscriminator;
|
||||
enum?: Array<any>;
|
||||
example?: any;
|
||||
exclusiveMaximum?: boolean;
|
||||
exclusiveMinimum?: boolean;
|
||||
externalDocs?: OpenApiExternalDocumentation;
|
||||
format?: string;
|
||||
items?: (OpenApiSchema | OpenApiReference);
|
||||
maxItems?: number;
|
||||
maxLength?: number;
|
||||
maxProperties?: number;
|
||||
maximum?: number;
|
||||
minItems?: number;
|
||||
minLength?: number;
|
||||
minProperties?: number;
|
||||
minimum?: number;
|
||||
multipleOf?: number;
|
||||
not?: (OpenApiSchema | OpenApiReference);
|
||||
nullable?: boolean;
|
||||
oneOf?: Array<(OpenApiSchema | OpenApiReference)>;
|
||||
pattern?: string;
|
||||
properties?: Record<string, (OpenApiSchema | OpenApiReference)>;
|
||||
readOnly?: boolean;
|
||||
required?: Array<string>;
|
||||
title?: string;
|
||||
type?: 'array' | 'boolean' | 'integer' | 'number' | 'object' | 'string';
|
||||
uniqueItems?: boolean;
|
||||
writeOnly?: boolean;
|
||||
xml?: OpenApiXml;
|
||||
};
|
||||
|
12
dc-agents/sqlite/src/types/models/OpenApiXml.ts
Normal file
12
dc-agents/sqlite/src/types/models/OpenApiXml.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type OpenApiXml = {
|
||||
attribute?: boolean;
|
||||
name?: string;
|
||||
namespace?: string;
|
||||
prefix?: string;
|
||||
wrapped?: boolean;
|
||||
};
|
||||
|
11
dc-agents/sqlite/src/types/models/OrExpression.ts
Normal file
11
dc-agents/sqlite/src/types/models/OrExpression.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Expression } from './Expression';
|
||||
|
||||
export type OrExpression = {
|
||||
expressions: Array<Expression>;
|
||||
type: 'or';
|
||||
};
|
||||
|
14
dc-agents/sqlite/src/types/models/OrderBy.ts
Normal file
14
dc-agents/sqlite/src/types/models/OrderBy.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { OrderType } from './OrderType';
|
||||
|
||||
export type OrderBy = {
|
||||
/**
|
||||
* Column to order by
|
||||
*/
|
||||
column: string;
|
||||
ordering: OrderType;
|
||||
};
|
||||
|
5
dc-agents/sqlite/src/types/models/OrderType.ts
Normal file
5
dc-agents/sqlite/src/types/models/OrderType.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type OrderType = 'asc' | 'desc';
|
33
dc-agents/sqlite/src/types/models/Query.ts
Normal file
33
dc-agents/sqlite/src/types/models/Query.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Aggregate } from './Aggregate';
|
||||
import type { Expression } from './Expression';
|
||||
import type { Field } from './Field';
|
||||
import type { OrderBy } from './OrderBy';
|
||||
|
||||
export type Query = {
|
||||
/**
|
||||
* Aggregate fields of the query
|
||||
*/
|
||||
aggregates?: Record<string, Aggregate> | null;
|
||||
/**
|
||||
* Fields of the query
|
||||
*/
|
||||
fields?: Record<string, Field> | null;
|
||||
/**
|
||||
* Optionally limit to N results
|
||||
*/
|
||||
limit?: number | null;
|
||||
/**
|
||||
* Optionally offset from the Nth result
|
||||
*/
|
||||
offset?: number | null;
|
||||
/**
|
||||
* Optionally order the results by the value of one or more fields
|
||||
*/
|
||||
order_by?: Array<OrderBy> | null;
|
||||
where?: Expression;
|
||||
};
|
||||
|
11
dc-agents/sqlite/src/types/models/QueryCapabilities.ts
Normal file
11
dc-agents/sqlite/src/types/models/QueryCapabilities.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type QueryCapabilities = {
|
||||
/**
|
||||
* Does the agent support querying a table by primary key?
|
||||
*/
|
||||
supportsPrimaryKeys: boolean;
|
||||
};
|
||||
|
17
dc-agents/sqlite/src/types/models/QueryRequest.ts
Normal file
17
dc-agents/sqlite/src/types/models/QueryRequest.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Query } from './Query';
|
||||
import type { TableName } from './TableName';
|
||||
import type { TableRelationships } from './TableRelationships';
|
||||
|
||||
export type QueryRequest = {
|
||||
query: Query;
|
||||
table: TableName;
|
||||
/**
|
||||
* The relationships between tables involved in the entire query request
|
||||
*/
|
||||
table_relationships: Array<TableRelationships>;
|
||||
};
|
||||
|
19
dc-agents/sqlite/src/types/models/QueryResponse.ts
Normal file
19
dc-agents/sqlite/src/types/models/QueryResponse.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ColumnFieldValue } from './ColumnFieldValue';
|
||||
import type { NullColumnFieldValue } from './NullColumnFieldValue';
|
||||
import type { ScalarValue } from './ScalarValue';
|
||||
|
||||
export type QueryResponse = {
|
||||
/**
|
||||
* The results of the aggregates returned by the query
|
||||
*/
|
||||
aggregates?: Record<string, ScalarValue> | null;
|
||||
/**
|
||||
* The rows returned by the query, corresponding to the query's fields
|
||||
*/
|
||||
rows?: Array<Record<string, (ColumnFieldValue | QueryResponse | NullColumnFieldValue)>> | null;
|
||||
};
|
||||
|
16
dc-agents/sqlite/src/types/models/Relationship.ts
Normal file
16
dc-agents/sqlite/src/types/models/Relationship.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RelationshipType } from './RelationshipType';
|
||||
import type { TableName } from './TableName';
|
||||
|
||||
export type Relationship = {
|
||||
/**
|
||||
* A mapping between columns on the source table to columns on the target table
|
||||
*/
|
||||
column_mapping: Record<string, string>;
|
||||
relationship_type: RelationshipType;
|
||||
target_table: TableName;
|
||||
};
|
||||
|
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type RelationshipCapabilities = {
|
||||
};
|
||||
|
15
dc-agents/sqlite/src/types/models/RelationshipField.ts
Normal file
15
dc-agents/sqlite/src/types/models/RelationshipField.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Query } from './Query';
|
||||
|
||||
export type RelationshipField = {
|
||||
query: Query;
|
||||
/**
|
||||
* The name of the relationship to follow for the subquery
|
||||
*/
|
||||
relationship: string;
|
||||
type: 'relationship';
|
||||
};
|
||||
|
5
dc-agents/sqlite/src/types/models/RelationshipType.ts
Normal file
5
dc-agents/sqlite/src/types/models/RelationshipType.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type RelationshipType = 'object' | 'array';
|
5
dc-agents/sqlite/src/types/models/ScalarType.ts
Normal file
5
dc-agents/sqlite/src/types/models/ScalarType.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ScalarType = 'string' | 'number' | 'bool';
|
6
dc-agents/sqlite/src/types/models/ScalarValue.ts
Normal file
6
dc-agents/sqlite/src/types/models/ScalarValue.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type ScalarValue = (string | number | boolean | null);
|
||||
|
11
dc-agents/sqlite/src/types/models/ScalarValueComparison.ts
Normal file
11
dc-agents/sqlite/src/types/models/ScalarValueComparison.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ScalarValue } from './ScalarValue';
|
||||
|
||||
export type ScalarValueComparison = {
|
||||
type: 'scalar';
|
||||
value: ScalarValue;
|
||||
};
|
||||
|
13
dc-agents/sqlite/src/types/models/SchemaResponse.ts
Normal file
13
dc-agents/sqlite/src/types/models/SchemaResponse.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { TableInfo } from './TableInfo';
|
||||
|
||||
export type SchemaResponse = {
|
||||
/**
|
||||
* Available tables
|
||||
*/
|
||||
tables: Array<TableInfo>;
|
||||
};
|
||||
|
15
dc-agents/sqlite/src/types/models/SingleColumnAggregate.ts
Normal file
15
dc-agents/sqlite/src/types/models/SingleColumnAggregate.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { SingleColumnAggregateFunction } from './SingleColumnAggregateFunction';
|
||||
|
||||
export type SingleColumnAggregate = {
|
||||
/**
|
||||
* The column to apply the aggregation function to
|
||||
*/
|
||||
column: string;
|
||||
function: SingleColumnAggregateFunction;
|
||||
type: 'single_column';
|
||||
};
|
||||
|
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type SingleColumnAggregateFunction = 'avg' | 'max' | 'min' | 'stddev_pop' | 'stddev_samp' | 'sum' | 'var_pop' | 'var_samp';
|
8
dc-agents/sqlite/src/types/models/StarCountAggregate.ts
Normal file
8
dc-agents/sqlite/src/types/models/StarCountAggregate.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type StarCountAggregate = {
|
||||
type: 'star_count';
|
||||
};
|
||||
|
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type SubscriptionCapabilities = {
|
||||
};
|
||||
|
23
dc-agents/sqlite/src/types/models/TableInfo.ts
Normal file
23
dc-agents/sqlite/src/types/models/TableInfo.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ColumnInfo } from './ColumnInfo';
|
||||
import type { TableName } from './TableName';
|
||||
|
||||
export type TableInfo = {
|
||||
/**
|
||||
* The columns of the table
|
||||
*/
|
||||
columns: Array<ColumnInfo>;
|
||||
/**
|
||||
* Description of the table
|
||||
*/
|
||||
description?: string | null;
|
||||
name: TableName;
|
||||
/**
|
||||
* The primary key of the table
|
||||
*/
|
||||
primary_key?: Array<string> | null;
|
||||
};
|
||||
|
8
dc-agents/sqlite/src/types/models/TableName.ts
Normal file
8
dc-agents/sqlite/src/types/models/TableName.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* The fully qualified name of a table, where the last item in the array is the table name and any earlier items represent the namespacing of the table name
|
||||
*/
|
||||
export type TableName = Array<string>;
|
15
dc-agents/sqlite/src/types/models/TableRelationships.ts
Normal file
15
dc-agents/sqlite/src/types/models/TableRelationships.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Relationship } from './Relationship';
|
||||
import type { TableName } from './TableName';
|
||||
|
||||
export type TableRelationships = {
|
||||
/**
|
||||
* A map of relationships from the source table to target tables. The key of the map is the relationship name
|
||||
*/
|
||||
relationships: Record<string, Relationship>;
|
||||
source_table: TableName;
|
||||
};
|
||||
|
@ -0,0 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type UnaryComparisonOperator = ('is_null' | string);
|
||||
|
60
dc-agents/sqlite/src/util.ts
Normal file
60
dc-agents/sqlite/src/util.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { TableName } from "./types";
|
||||
|
||||
export const coerceUndefinedToNull = <T>(v: T | undefined): T | null => v === undefined ? null : v;
|
||||
|
||||
export const coerceUndefinedOrNullToEmptyArray = <T>(v: Array<T> | undefined | null): Array<T> => v == null ? [] : v;
|
||||
|
||||
export const coerceUndefinedOrNullToEmptyRecord = <K extends string | number | symbol, V>(v: Record<K,V> | undefined | null): Record<K,V> => v == null ? {} as Record<K,V> : v;
|
||||
|
||||
export const unreachable = (x: never): never => { throw new Error(`Unreachable code reached! The types lied! 😭 Unexpected value: ${x}`) };
|
||||
|
||||
export const zip = <T, U>(arr1: T[], arr2: U[]): [T,U][] => {
|
||||
const length = Math.min(arr1.length, arr2.length);
|
||||
const newArray = Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
newArray[i] = [arr1[i], arr2[i]];
|
||||
}
|
||||
return newArray;
|
||||
};
|
||||
|
||||
export const crossProduct = <T, U>(arr1: T[], arr2: U[]): [T,U][] => {
|
||||
return arr1.flatMap(a1 => arr2.map(a2 => [a1, a2]) as [T,U][]);
|
||||
};
|
||||
|
||||
export function omap<V,O>(m: { [x: string]: V; },f: (k: string, v: V) => O) {
|
||||
return Object.keys(m).map(k => f(k, m[k]))
|
||||
}
|
||||
|
||||
export function stringToBool(x: string | null | undefined): boolean {
|
||||
return (/1|true|t|yes|y/i).test(x || '');
|
||||
}
|
||||
|
||||
export function last<T>(x: Array<T>): T {
|
||||
return x[x.length - 1];
|
||||
}
|
||||
|
||||
export function logDeep(msg: string, myObject: any): void {
|
||||
const util = require('util');
|
||||
console.log(msg, util.inspect(myObject, {showHidden: true, depth: null, colors: true}));
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj: Record<string, any>): boolean {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage: `await delay(5000)`
|
||||
*
|
||||
* @param ms
|
||||
* @returns
|
||||
*/
|
||||
export function delay(ms: number): Promise<void> {
|
||||
return new Promise( resolve => setTimeout(resolve, ms) );
|
||||
}
|
||||
|
||||
export const tableNameEquals = (tableName1: TableName) => (tableName2: TableName): boolean => {
|
||||
if (tableName1.length !== tableName2.length)
|
||||
return false;
|
||||
|
||||
return zip(tableName1, tableName2).every(([n1, n2]) => n1 === n2);
|
||||
}
|
BIN
dc-agents/sqlite/test/db.chinook.sqlite
Normal file
BIN
dc-agents/sqlite/test/db.chinook.sqlite
Normal file
Binary file not shown.
10
dc-agents/sqlite/tsconfig.json
Normal file
10
dc-agents/sqlite/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "@tsconfig/node16/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"resolveJsonModule": true,
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
@ -83,6 +83,13 @@ services:
|
||||
ports:
|
||||
- "65005:8100"
|
||||
|
||||
dc-sqlite-agent:
|
||||
build: ./dc-agents/sqlite/
|
||||
ports:
|
||||
- "65006:8100"
|
||||
volumes:
|
||||
- "./dc-agents/sqlite/test/db.chinook.sqlite:/db.chinook.sqlite"
|
||||
|
||||
volumes:
|
||||
citus-data:
|
||||
mariadb-data:
|
||||
|
Loading…
Reference in New Issue
Block a user