mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-11 10:46:25 +03:00
Add mutability properties to the Data Connector schema API [GDC-664]
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7190 GitOrigin-RevId: ce602b5e5cc5aee8716ff3f7a036b18b3bf47188
This commit is contained in:
parent
1dd9e19b69
commit
c14fd3ba4c
@ -267,6 +267,7 @@ The `GET /schema` endpoint is called whenever the metadata is (re)loaded by `gra
|
||||
"tables": [
|
||||
{
|
||||
"name": ["Artist"],
|
||||
"type": "table",
|
||||
"primary_key": ["ArtistId"],
|
||||
"description": "Collection of artists of music",
|
||||
"columns": [
|
||||
@ -274,18 +275,26 @@ The `GET /schema` endpoint is called whenever the metadata is (re)loaded by `gra
|
||||
"name": "ArtistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Artist primary key identifier"
|
||||
"description": "Artist primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the artist"
|
||||
"description": "The name of the artist",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Album"],
|
||||
"type": "table",
|
||||
"primary_key": ["AlbumId"],
|
||||
"description": "Collection of music albums created by artists",
|
||||
"columns": [
|
||||
@ -293,32 +302,49 @@ The `GET /schema` endpoint is called whenever the metadata is (re)loaded by `gra
|
||||
"name": "AlbumId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Album primary key identifier"
|
||||
"description": "Album primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false,
|
||||
},
|
||||
{
|
||||
"name": "Title",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The title of the album"
|
||||
"description": "The title of the album",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "ArtistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the artist that created this album"
|
||||
"description": "The ID of the artist that created this album",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `tables` section describes the two available tables, as well as their columns, including types and nullability information.
|
||||
The `tables` section describes two available tables, as well as their columns, including types and nullability information.
|
||||
|
||||
Notice that the names of tables and columns are used in the metadata document to describe tracked tables and relationships.
|
||||
|
||||
Table names are described as an array of strings. This allows agents to fully qualify their table names with whatever namespacing requirements they have. For example, if the agent connects to a database that puts tables inside schemas, the agent could use table names such as `["my_schema", "my_table"]`.
|
||||
|
||||
The `type` of a table can be either a "table" or a "view".
|
||||
|
||||
Tables have mutability properties, namely, whether they are "insertable", "updatable" and "deletable", which refers to whether rows can be inserted/updated/deleted from the table. Typically, in an RDBMS, tables are insertable, updatable and deletable, but views may not be. However, an agent may declare the mutability properties in any combination that suits its data source.
|
||||
|
||||
Columns also have "insertable" and "updatable" mutability properties. Typically, in an RDBMS, computed columns are neither insertable not updatable, primary keys are insertable but not updatable and normal columns are insertable and updatable. Agents may declare whatever combination suits their data source.
|
||||
|
||||
If the agent declares a lack of mutability support in its capabilities, it should not declare tables/columns as mutable in its schema here.
|
||||
|
||||
#### Type definitions
|
||||
|
||||
The `SchemaResponse` TypeScript type from [the reference implementation](./reference/src/types/index.ts) describes the valid response body for the `GET /schema` endpoint.
|
||||
@ -2134,4 +2160,3 @@ Breaking down the properties in the `delete`-typed mutation operation:
|
||||
* `returning_fields`: This specifies a list of fields to return in the response. The property takes the same format as the `fields` property on Queries. It is expected that the specified fields will be returned for all rows affected by the deletion (ie. all deleted rows).
|
||||
|
||||
Delete operations return responses that are the same as insert and update operations, except the affected rows in `returning` are the deleted rows instead.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"description": "Hasura GraphQL Engine Data Connector Agent API types",
|
||||
"author": "Hasura (https://github.com/hasura/graphql-engine)",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -934,6 +934,13 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"TableType": {
|
||||
"enum": [
|
||||
"table",
|
||||
"view"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ColumnInfo": {
|
||||
"properties": {
|
||||
"description": {
|
||||
@ -941,6 +948,11 @@
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"insertable": {
|
||||
"default": false,
|
||||
"description": "Whether or not the column can be inserted into",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"description": "Column name",
|
||||
"type": "string"
|
||||
@ -951,6 +963,11 @@
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/ScalarType"
|
||||
},
|
||||
"updatable": {
|
||||
"default": false,
|
||||
"description": "Whether or not the column can be updated",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@ -988,6 +1005,11 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"deletable": {
|
||||
"default": false,
|
||||
"description": "Whether or not existing rows can be deleted in the table",
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"description": "Description of the table",
|
||||
"nullable": true,
|
||||
@ -1001,6 +1023,11 @@
|
||||
"description": "Foreign key constraints",
|
||||
"type": "object"
|
||||
},
|
||||
"insertable": {
|
||||
"default": false,
|
||||
"description": "Whether or not new rows can be inserted into the table",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"$ref": "#/components/schemas/TableName"
|
||||
},
|
||||
@ -1011,6 +1038,14 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/TableType"
|
||||
},
|
||||
"updatable": {
|
||||
"default": false,
|
||||
"description": "Whether or not existing rows can be updated in the table",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -101,6 +101,7 @@ export type { TableInfo } from './models/TableInfo';
|
||||
export type { TableInsertSchema } from './models/TableInsertSchema';
|
||||
export type { TableName } from './models/TableName';
|
||||
export type { TableRelationships } from './models/TableRelationships';
|
||||
export type { TableType } from './models/TableType';
|
||||
export type { UnaryComparisonOperator } from './models/UnaryComparisonOperator';
|
||||
export type { UnrelatedTable } from './models/UnrelatedTable';
|
||||
export type { UpdateCapabilities } from './models/UpdateCapabilities';
|
||||
|
@ -9,6 +9,10 @@ export type ColumnInfo = {
|
||||
* Column description
|
||||
*/
|
||||
description?: string | null;
|
||||
/**
|
||||
* Whether or not the column can be inserted into
|
||||
*/
|
||||
insertable?: boolean;
|
||||
/**
|
||||
* Column name
|
||||
*/
|
||||
@ -18,5 +22,9 @@ export type ColumnInfo = {
|
||||
*/
|
||||
nullable: boolean;
|
||||
type: ScalarType;
|
||||
/**
|
||||
* Whether or not the column can be updated
|
||||
*/
|
||||
updatable?: boolean;
|
||||
};
|
||||
|
||||
|
@ -5,12 +5,17 @@
|
||||
import type { ColumnInfo } from './ColumnInfo';
|
||||
import type { Constraint } from './Constraint';
|
||||
import type { TableName } from './TableName';
|
||||
import type { TableType } from './TableType';
|
||||
|
||||
export type TableInfo = {
|
||||
/**
|
||||
* The columns of the table
|
||||
*/
|
||||
columns: Array<ColumnInfo>;
|
||||
/**
|
||||
* Whether or not existing rows can be deleted in the table
|
||||
*/
|
||||
deletable?: boolean;
|
||||
/**
|
||||
* Description of the table
|
||||
*/
|
||||
@ -19,10 +24,19 @@ export type TableInfo = {
|
||||
* Foreign key constraints
|
||||
*/
|
||||
foreign_keys?: Record<string, Constraint>;
|
||||
/**
|
||||
* Whether or not new rows can be inserted into the table
|
||||
*/
|
||||
insertable?: boolean;
|
||||
name: TableName;
|
||||
/**
|
||||
* The primary key of the table
|
||||
*/
|
||||
primary_key?: Array<string>;
|
||||
type?: TableType;
|
||||
/**
|
||||
* Whether or not existing rows can be updated in the table
|
||||
*/
|
||||
updatable?: boolean;
|
||||
};
|
||||
|
||||
|
5
dc-agents/dc-api-types/src/models/TableType.ts
Normal file
5
dc-agents/dc-api-types/src/models/TableType.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type TableType = 'table' | 'view';
|
10
dc-agents/package-lock.json
generated
10
dc-agents/package-lock.json
generated
@ -24,7 +24,7 @@
|
||||
},
|
||||
"dc-api-types": {
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
@ -1197,7 +1197,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^7.0.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify": "^3.29.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -1781,7 +1781,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify": "^4.4.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -3125,7 +3125,7 @@
|
||||
"version": "file:reference",
|
||||
"requires": {
|
||||
"@fastify/cors": "^7.0.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
@ -3514,7 +3514,7 @@
|
||||
"version": "file:sqlite",
|
||||
"requires": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/sqlite3": "^3.1.8",
|
||||
|
4
dc-agents/reference/package-lock.json
generated
4
dc-agents/reference/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^7.0.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify": "^3.29.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -44,7 +44,7 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^7.0.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify": "^3.29.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
|
@ -100,6 +100,7 @@ const schema: SchemaResponse = {
|
||||
tables: [
|
||||
{
|
||||
name: ["Artist"],
|
||||
type: "table",
|
||||
primary_key: ["ArtistId"],
|
||||
description: "Collection of artists of music",
|
||||
columns: [
|
||||
@ -107,18 +108,26 @@ const schema: SchemaResponse = {
|
||||
name: "ArtistId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Artist primary key identifier"
|
||||
description: "Artist primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Name",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The name of the artist"
|
||||
description: "The name of the artist",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
}
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Album"],
|
||||
type: "table",
|
||||
primary_key: ["AlbumId"],
|
||||
foreign_keys: {
|
||||
"Artist": {
|
||||
@ -134,24 +143,34 @@ const schema: SchemaResponse = {
|
||||
name: "AlbumId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Album primary key identifier"
|
||||
description: "Album primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Title",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The title of the album"
|
||||
description: "The title of the album",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "ArtistId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The ID of the artist that created this album"
|
||||
description: "The ID of the artist that created this album",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
}
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Customer"],
|
||||
type: "table",
|
||||
primary_key: ["CustomerId"],
|
||||
foreign_keys: {
|
||||
"CustomerSupportRep": {
|
||||
@ -167,84 +186,114 @@ const schema: SchemaResponse = {
|
||||
name: "CustomerId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Customer primary key identifier"
|
||||
description: "Customer primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "FirstName",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The customer's first name"
|
||||
description: "The customer's first name",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "LastName",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The customer's last name"
|
||||
description: "The customer's last name",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Company",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's company name"
|
||||
description: "The customer's company name",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Address",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's address line (street number, street)"
|
||||
description: "The customer's address line (street number, street)",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "City",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's address city"
|
||||
description: "The customer's address city",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "State",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's address state"
|
||||
description: "The customer's address state",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Country",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's address country"
|
||||
description: "The customer's address country",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "PostalCode",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's address postal code"
|
||||
description: "The customer's address postal code",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Phone",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's phone number"
|
||||
description: "The customer's phone number",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Fax",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The customer's fax number"
|
||||
description: "The customer's fax number",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Email",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The customer's email address"
|
||||
description: "The customer's email address",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "SupportRepId",
|
||||
type: "number",
|
||||
nullable: true,
|
||||
description: "The ID of the Employee who is this customer's support representative"
|
||||
description: "The ID of the Employee who is this customer's support representative",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
}
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Employee"],
|
||||
type: "table",
|
||||
primary_key: ["EmployeeId"],
|
||||
foreign_keys: {
|
||||
"EmployeeReportsTo": {
|
||||
@ -260,96 +309,130 @@ const schema: SchemaResponse = {
|
||||
name: "EmployeeId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Employee primary key identifier"
|
||||
description: "Employee primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "LastName",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The employee's last name"
|
||||
description: "The employee's last name",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "FirstName",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The employee's first name"
|
||||
description: "The employee's first name",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Title",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's job title"
|
||||
description: "The employee's job title",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "ReportsTo",
|
||||
type: "number",
|
||||
nullable: true,
|
||||
description: "The employee's manager"
|
||||
description: "The employee's manager",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BirthDate",
|
||||
type: "DateTime",
|
||||
nullable: true,
|
||||
description: "The employee's birth date"
|
||||
description: "The employee's birth date",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "HireDate",
|
||||
type: "DateTime",
|
||||
nullable: true,
|
||||
description: "The employee's hire date"
|
||||
description: "The employee's hire date",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Address",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's address line (street number, street)"
|
||||
description: "The employee's address line (street number, street)",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "City",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's address city"
|
||||
description: "The employee's address city",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "State",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's address state"
|
||||
description: "The employee's address state",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Country",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's address country"
|
||||
description: "The employee's address country",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "PostalCode",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's address postal code"
|
||||
description: "The employee's address postal code",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Phone",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's phone number"
|
||||
description: "The employee's phone number",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Fax",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's fax number"
|
||||
description: "The employee's fax number",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Email",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The employee's email address"
|
||||
description: "The employee's email address",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Genre"],
|
||||
type: "table",
|
||||
primary_key: ["GenreId"],
|
||||
description: "Genres of music",
|
||||
columns: [
|
||||
@ -357,18 +440,26 @@ const schema: SchemaResponse = {
|
||||
name: "GenreId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Genre primary key identifier"
|
||||
description: "Genre primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Name",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The name of the genre"
|
||||
description: "The name of the genre",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
}
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Invoice"],
|
||||
type: "table",
|
||||
primary_key: ["InvoiceId"],
|
||||
foreign_keys: {
|
||||
"InvoiceCustomer": {
|
||||
@ -384,60 +475,82 @@ const schema: SchemaResponse = {
|
||||
name: "InvoiceId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Invoice primary key identifier"
|
||||
description: "Invoice primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "CustomerId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "ID of the customer who bought the music"
|
||||
description: "ID of the customer who bought the music",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "InvoiceDate",
|
||||
type: "DateTime",
|
||||
nullable: false,
|
||||
description: "Date of the invoice"
|
||||
description: "Date of the invoice",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BillingAddress",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The invoice's billing address line (street number, street)"
|
||||
description: "The invoice's billing address line (street number, street)",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BillingCity",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The invoice's billing address city"
|
||||
description: "The invoice's billing address city",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BillingState",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The invoice's billing address state"
|
||||
description: "The invoice's billing address state",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BillingCountry",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The invoice's billing address country"
|
||||
description: "The invoice's billing address country",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "BillingPostalCode",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The invoice's billing address postal code"
|
||||
description: "The invoice's billing address postal code",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Total",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The total amount due on the invoice"
|
||||
description: "The total amount due on the invoice",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["InvoiceLine"],
|
||||
type: "table",
|
||||
primary_key: ["InvoiceLineId"],
|
||||
foreign_keys: {
|
||||
"Invoice": {
|
||||
@ -459,36 +572,50 @@ const schema: SchemaResponse = {
|
||||
name: "InvoiceLineId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Invoice Line primary key identifier"
|
||||
description: "Invoice Line primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "InvoiceId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "ID of the invoice the line belongs to"
|
||||
description: "ID of the invoice the line belongs to",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "TrackId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "ID of the music track being purchased"
|
||||
description: "ID of the music track being purchased",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "UnitPrice",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Price of each individual track unit"
|
||||
description: "Price of each individual track unit",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Quantity",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Quantity of the track purchased"
|
||||
description: "Quantity of the track purchased",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["MediaType"],
|
||||
type: "table",
|
||||
primary_key: ["MediaTypeId"],
|
||||
description: "Collection of media types that tracks can be encoded in",
|
||||
columns: [
|
||||
@ -496,18 +623,26 @@ const schema: SchemaResponse = {
|
||||
name: "MediaTypeId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Media Type primary key identifier"
|
||||
description: "Media Type primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Name",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The name of the media type format"
|
||||
description: "The name of the media type format",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Playlist"],
|
||||
type: "table",
|
||||
primary_key: ["PlaylistId"],
|
||||
description: "Collection of playlists",
|
||||
columns: [
|
||||
@ -515,18 +650,26 @@ const schema: SchemaResponse = {
|
||||
name: "PlaylistId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "Playlist primary key identifier"
|
||||
description: "Playlist primary key identifier",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Name",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The name of the playlist"
|
||||
description: "The name of the playlist",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["PlaylistTrack"],
|
||||
type: "table",
|
||||
primary_key: ["PlaylistId", "TrackId"],
|
||||
foreign_keys: {
|
||||
"Playlist": {
|
||||
@ -548,18 +691,26 @@ const schema: SchemaResponse = {
|
||||
name: "PlaylistId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The ID of the playlist"
|
||||
description: "The ID of the playlist",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "TrackId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The ID of the track"
|
||||
description: "The ID of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
{
|
||||
name: ["Track"],
|
||||
type: "table",
|
||||
primary_key: ["TrackId"],
|
||||
foreign_keys: {
|
||||
"Album": {
|
||||
@ -587,57 +738,78 @@ const schema: SchemaResponse = {
|
||||
name: "TrackId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The ID of the track"
|
||||
description: "The ID of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Name",
|
||||
type: "string",
|
||||
nullable: false,
|
||||
description: "The name of the track"
|
||||
description: "The name of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "AlbumId",
|
||||
type: "number",
|
||||
nullable: true,
|
||||
description: "The ID of the album the track belongs to"
|
||||
description: "The ID of the album the track belongs to",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "MediaTypeId",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The ID of the media type the track is encoded with"
|
||||
description: "The ID of the media type the track is encoded with",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "GenreId",
|
||||
type: "number",
|
||||
nullable: true,
|
||||
description: "The ID of the genre of the track"
|
||||
description: "The ID of the genre of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Composer",
|
||||
type: "string",
|
||||
nullable: true,
|
||||
description: "The name of the composer of the track"
|
||||
description: "The name of the composer of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Milliseconds",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The length of the track in milliseconds"
|
||||
description: "The length of the track in milliseconds",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "Bytes",
|
||||
type: "number",
|
||||
nullable: true,
|
||||
description: "The size of the track in bytes"
|
||||
description: "The size of the track in bytes",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
{
|
||||
name: "UnitPrice",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
description: "The price of the track"
|
||||
description: "The price of the track",
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
},
|
||||
]
|
||||
],
|
||||
insertable: false,
|
||||
updatable: false,
|
||||
deletable: false,
|
||||
},
|
||||
]
|
||||
};
|
||||
|
4
dc-agents/sqlite/package-lock.json
generated
4
dc-agents/sqlite/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify": "^4.4.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -54,7 +54,7 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.18.0",
|
||||
"@hasura/dc-api-types": "0.19.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"fastify": "^4.4.0",
|
||||
"nanoid": "^3.3.4",
|
||||
|
@ -22,6 +22,19 @@ export const capabilitiesResponse: CapabilitiesResponse = {
|
||||
supports_relations: true
|
||||
}
|
||||
},
|
||||
... (
|
||||
envToBool('MUTATIONS')
|
||||
? {
|
||||
mutations: {
|
||||
atomicity_support_level: "heterogeneous_operations",
|
||||
insert: { supports_nested_inserts: true },
|
||||
update: {},
|
||||
delete: {},
|
||||
returning: {},
|
||||
}
|
||||
}
|
||||
: {}
|
||||
),
|
||||
explain: {},
|
||||
raw: {},
|
||||
... ( envToBool('METRICS') ? { metrics: {} } : {} )
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { SchemaResponse, ScalarType, ColumnInfo, TableInfo, Constraint } from "@hasura/dc-api-types"
|
||||
import { Config } from "./config";
|
||||
import { connect, SqlLogger } from './db';
|
||||
import { logDeep } from "./util";
|
||||
import { envToBool } from "./util";
|
||||
|
||||
var sqliteParser = require('sqlite-parser');
|
||||
|
||||
@ -35,13 +35,17 @@ function determineScalarType(datatype: Datatype): ScalarType {
|
||||
}
|
||||
}
|
||||
|
||||
function getColumns(ast: any[]) : ColumnInfo[] {
|
||||
function getColumns(ast: any[], primaryKeys: string[], mutationsEnabled: boolean) : ColumnInfo[] {
|
||||
return ast.map(c => {
|
||||
return ({
|
||||
const isPrimaryKey = primaryKeys.includes(c.name);
|
||||
|
||||
return {
|
||||
name: c.name,
|
||||
type: determineScalarType(c.datatype),
|
||||
nullable: nullableCast(c.definition)
|
||||
})
|
||||
nullable: nullableCast(c.definition),
|
||||
insertable: mutationsEnabled,
|
||||
updatable: mutationsEnabled && !isPrimaryKey,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@ -56,20 +60,25 @@ function nullableCast(ds: any[]): boolean {
|
||||
|
||||
const formatTableInfo = (config: Config) => (info: TableInfoInternal): TableInfo => {
|
||||
const ast = sqliteParser(info.sql);
|
||||
const ddl = ddlColumns(ast);
|
||||
const columnsDdl = getColumnsDdl(ast);
|
||||
const primaryKeys = ddlPKs(ast);
|
||||
const foreignKeys = ddlFKs(config, ast);
|
||||
const primaryKey = primaryKeys.length > 0 ? { primary_key: primaryKeys } : {};
|
||||
const foreignKey = foreignKeys.length > 0 ? { foreign_keys: Object.fromEntries(foreignKeys) } : {};
|
||||
const tableName = config.explicit_main_schema ? ["main", info.name] : [info.name];
|
||||
|
||||
// TODO: Should we include something for the description here?
|
||||
const mutationsEnabled = envToBool('MUTATIONS');
|
||||
|
||||
return {
|
||||
name: tableName,
|
||||
type: "table",
|
||||
...primaryKey,
|
||||
...foreignKey,
|
||||
description: info.sql,
|
||||
columns: getColumns(ddl)
|
||||
columns: getColumns(columnsDdl, primaryKeys, mutationsEnabled),
|
||||
insertable: mutationsEnabled,
|
||||
updatable: mutationsEnabled,
|
||||
deletable: mutationsEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +109,7 @@ function includeTable(config: Config, table: TableInfoInternal): boolean {
|
||||
* @param ddl - The output of sqlite-parser
|
||||
* @returns - List of columns as present in the output of sqlite-parser.
|
||||
*/
|
||||
function ddlColumns(ddl: any): any[] {
|
||||
function getColumnsDdl(ddl: any): any[] {
|
||||
if(ddl.type != 'statement' || ddl.variant != 'list') {
|
||||
throw new Error("Encountered a non-statement or non-list when parsing DDL for table.");
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import Control.Lens qualified as Lens
|
||||
import Data.Aeson qualified as J
|
||||
import Data.Aeson.KeyMap qualified as KM
|
||||
import Data.Aeson.Lens
|
||||
import Data.Aeson.Types qualified as J
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Data.Vector qualified as Vector
|
||||
import Harness.Backend.DataConnector.Chinook qualified as Chinook
|
||||
@ -24,6 +25,7 @@ import Harness.Test.Fixture qualified as Fixture
|
||||
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.TestEnvironment qualified as TestEnvironment
|
||||
import Harness.Yaml (shouldReturnYaml, shouldReturnYamlF)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.Hspec (SpecWith, describe, it, pendingWith)
|
||||
|
||||
@ -103,6 +105,14 @@ schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
|
||||
let removeDescriptions (J.Object o) = J.Object (KM.delete "description" (removeDescriptions <$> o))
|
||||
removeDescriptions (J.Array a) = J.Array (removeDescriptions <$> a)
|
||||
removeDescriptions x = x
|
||||
let mutationsCapabilities =
|
||||
TestEnvironment.backendTypeConfig testEnvironment
|
||||
>>= BackendType.backendCapabilities
|
||||
>>= J.parseMaybe J.parseJSON
|
||||
>>= API._cMutations
|
||||
let supportsInserts = isJust $ mutationsCapabilities >>= API._mcInsertCapabilities
|
||||
let supportsUpdates = isJust $ mutationsCapabilities >>= API._mcUpdateCapabilities
|
||||
let supportsDeletes = isJust $ mutationsCapabilities >>= API._mcDeleteCapabilities
|
||||
|
||||
case BackendType.backendSourceName <$> TestEnvironment.backendTypeConfig testEnvironment of
|
||||
Nothing -> pendingWith "Backend not found for testEnvironment"
|
||||
@ -125,13 +135,21 @@ schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
|
||||
- name: GenreId
|
||||
nullable: false
|
||||
type: number
|
||||
insertable: *supportsInserts
|
||||
updatable: false
|
||||
- name: Name
|
||||
nullable: true
|
||||
type: string
|
||||
insertable: *supportsInserts
|
||||
updatable: *supportsUpdates
|
||||
name:
|
||||
- Genre
|
||||
type: table
|
||||
primary_key:
|
||||
- GenreId
|
||||
insertable: *supportsInserts
|
||||
updatable: *supportsUpdates
|
||||
deletable: *supportsDeletes
|
||||
|]
|
||||
|
||||
describe "get_source_kind_capabilities" $ do
|
||||
|
@ -1,13 +1,14 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
--
|
||||
module Hasura.Backends.DataConnector.API.V0.Column
|
||||
( ColumnInfo (..),
|
||||
ciName,
|
||||
ciType,
|
||||
ciNullable,
|
||||
ciDescription,
|
||||
ciInsertable,
|
||||
ciUpdatable,
|
||||
ColumnName (..),
|
||||
)
|
||||
where
|
||||
@ -44,7 +45,9 @@ data ColumnInfo = ColumnInfo
|
||||
{ _ciName :: ColumnName,
|
||||
_ciType :: API.V0.Scalar.ScalarType,
|
||||
_ciNullable :: Bool,
|
||||
_ciDescription :: Maybe Text
|
||||
_ciDescription :: Maybe Text,
|
||||
_ciInsertable :: Bool,
|
||||
_ciUpdatable :: Bool
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
@ -58,5 +61,7 @@ instance HasCodec ColumnInfo where
|
||||
<*> requiredField "type" "Column type" .= _ciType
|
||||
<*> requiredField "nullable" "Is column nullable" .= _ciNullable
|
||||
<*> optionalFieldOrNull "description" "Column description" .= _ciDescription
|
||||
<*> optionalFieldWithDefault "insertable" False "Whether or not the column can be inserted into" .= _ciInsertable
|
||||
<*> optionalFieldWithDefault "updatable" False "Whether or not the column can be updated" .= _ciUpdatable
|
||||
|
||||
$(makeLenses ''ColumnInfo)
|
||||
|
@ -1,15 +1,20 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE OverloadedLists #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
--
|
||||
module Hasura.Backends.DataConnector.API.V0.Table
|
||||
( TableInfo (..),
|
||||
( TableName (..),
|
||||
TableInfo (..),
|
||||
tiName,
|
||||
tiType,
|
||||
tiColumns,
|
||||
tiPrimaryKey,
|
||||
tiForeignKeys,
|
||||
tiDescription,
|
||||
TableName (..),
|
||||
tiInsertable,
|
||||
tiUpdatable,
|
||||
tiDeletable,
|
||||
TableType (..),
|
||||
ForeignKeys (..),
|
||||
ConstraintName (..),
|
||||
Constraint (..),
|
||||
@ -56,10 +61,14 @@ instance HasCodec TableName where
|
||||
-- | Table schema data from the 'SchemaResponse'.
|
||||
data TableInfo = TableInfo
|
||||
{ _tiName :: TableName,
|
||||
_tiType :: TableType,
|
||||
_tiColumns :: [API.V0.ColumnInfo],
|
||||
_tiPrimaryKey :: [API.V0.ColumnName],
|
||||
_tiForeignKeys :: ForeignKeys,
|
||||
_tiDescription :: Maybe Text
|
||||
_tiDescription :: Maybe Text,
|
||||
_tiInsertable :: Bool,
|
||||
_tiUpdatable :: Bool,
|
||||
_tiDeletable :: Bool
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
@ -70,10 +79,31 @@ instance HasCodec TableInfo where
|
||||
object "TableInfo" $
|
||||
TableInfo
|
||||
<$> requiredField "name" "The name of the table" .= _tiName
|
||||
<*> optionalFieldWithDefault "type" Table "The type of table" .= _tiType
|
||||
<*> requiredField "columns" "The columns of the table" .= _tiColumns
|
||||
<*> optionalFieldWithOmittedDefault "primary_key" [] "The primary key of the table" .= _tiPrimaryKey
|
||||
<*> optionalFieldWithOmittedDefault "foreign_keys" (ForeignKeys mempty) "Foreign key constraints" .= _tiForeignKeys
|
||||
<*> optionalFieldOrNull "description" "Description of the table" .= _tiDescription
|
||||
<*> optionalFieldWithDefault "insertable" False "Whether or not new rows can be inserted into the table" .= _tiInsertable
|
||||
<*> optionalFieldWithDefault "updatable" False "Whether or not existing rows can be updated in the table" .= _tiUpdatable
|
||||
<*> optionalFieldWithDefault "deletable" False "Whether or not existing rows can be deleted in the table" .= _tiDeletable
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
data TableType
|
||||
= Table
|
||||
| View
|
||||
deriving stock (Eq, Ord, Show, Generic, Enum, Bounded)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
|
||||
instance HasCodec TableType where
|
||||
codec =
|
||||
named "TableType" $
|
||||
( stringConstCodec
|
||||
[ (Table, "table"),
|
||||
(View, "view")
|
||||
]
|
||||
)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -85,6 +115,8 @@ newtype ForeignKeys = ForeignKeys {unForeignKeys :: HashMap ConstraintName Const
|
||||
instance HasCodec ForeignKeys where
|
||||
codec = dimapCodec ForeignKeys unForeignKeys $ codec @(HashMap ConstraintName Constraint)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
newtype ConstraintName = ConstraintName {unConstraintName :: Text}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
deriving newtype (FromJSON, ToJSON, FromJSONKey, ToJSONKey)
|
||||
@ -105,5 +137,7 @@ instance HasCodec Constraint where
|
||||
<$> requiredField "foreign_table" "The table referenced by the foreign key in the child table." .= _cForeignTable
|
||||
<*> requiredField "column_mapping" "The columns on which you want want to define the foreign key." .= _cColumnMapping
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
$(makeLenses ''TableInfo)
|
||||
$(makeLenses ''Constraint)
|
||||
|
@ -1,6 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": ["Artist"],
|
||||
"type": "table",
|
||||
"primary_key": ["ArtistId"],
|
||||
"description": "Collection of artists of music",
|
||||
"columns": [
|
||||
@ -8,23 +9,31 @@
|
||||
"name": "ArtistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Artist primary key identifier"
|
||||
"description": "Artist primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the artist"
|
||||
"description": "The name of the artist",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Album"],
|
||||
"type": "table",
|
||||
"primary_key": ["AlbumId"],
|
||||
"foreign_keys": {
|
||||
"Artist": {
|
||||
"column_mapping": {
|
||||
"ArtistId": "ArtistId"
|
||||
"ArtistId": "ArtistId"
|
||||
},
|
||||
"foreign_table": ["Artist"]
|
||||
}
|
||||
@ -35,24 +44,34 @@
|
||||
"name": "AlbumId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Album primary key identifier"
|
||||
"description": "Album primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Title",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The title of the album"
|
||||
"description": "The title of the album",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "ArtistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the artist that created this album"
|
||||
"description": "The ID of the artist that created this album",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Customer"],
|
||||
"type": "table",
|
||||
"primary_key": ["CustomerId"],
|
||||
"foreign_keys": {
|
||||
"CustomerSupportRep": {
|
||||
@ -68,84 +87,114 @@
|
||||
"name": "CustomerId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Customer primary key identifier"
|
||||
"description": "Customer primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "FirstName",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The customer's first name"
|
||||
"description": "The customer's first name",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "LastName",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The customer's last name"
|
||||
"description": "The customer's last name",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Company",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's company name"
|
||||
"description": "The customer's company name",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Address",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's address line (street number, street)"
|
||||
"description": "The customer's address line (street number, street)",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "City",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's address city"
|
||||
"description": "The customer's address city",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "State",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's address state"
|
||||
"description": "The customer's address state",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Country",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's address country"
|
||||
"description": "The customer's address country",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "PostalCode",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's address postal code"
|
||||
"description": "The customer's address postal code",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Phone",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's phone number"
|
||||
"description": "The customer's phone number",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Fax",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The customer's fax number"
|
||||
"description": "The customer's fax number",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Email",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The customer's email address"
|
||||
"description": "The customer's email address",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "SupportRepId",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "The ID of the Employee who is this customer's support representative"
|
||||
"description": "The ID of the Employee who is this customer's support representative",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Employee"],
|
||||
"type": "table",
|
||||
"primary_key": ["EmployeeId"],
|
||||
"foreign_keys": {
|
||||
"EmployeeReportsTo": {
|
||||
@ -161,96 +210,130 @@
|
||||
"name": "EmployeeId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Employee primary key identifier"
|
||||
"description": "Employee primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "LastName",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The employee's last name"
|
||||
"description": "The employee's last name",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "FirstName",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The employee's first name"
|
||||
"description": "The employee's first name",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Title",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's job title"
|
||||
"description": "The employee's job title",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "ReportsTo",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "The employee's report"
|
||||
"description": "The employee's manager",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BirthDate",
|
||||
"type": "DateTime",
|
||||
"nullable": true,
|
||||
"description": "The employee's birth date"
|
||||
"description": "The employee's birth date",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "HireDate",
|
||||
"type": "DateTime",
|
||||
"nullable": true,
|
||||
"description": "The employee's hire date"
|
||||
"description": "The employee's hire date",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Address",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's address line (street number, street)"
|
||||
"description": "The employee's address line (street number, street)",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "City",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's address city"
|
||||
"description": "The employee's address city",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "State",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's address state"
|
||||
"description": "The employee's address state",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Country",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's address country"
|
||||
"description": "The employee's address country",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "PostalCode",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's address postal code"
|
||||
"description": "The employee's address postal code",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Phone",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's phone number"
|
||||
"description": "The employee's phone number",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Fax",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's fax number"
|
||||
"description": "The employee's fax number",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Email",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The employee's email address"
|
||||
"description": "The employee's email address",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Genre"],
|
||||
"type": "table",
|
||||
"primary_key": ["GenreId"],
|
||||
"description": "Genres of music",
|
||||
"columns": [
|
||||
@ -258,18 +341,26 @@
|
||||
"name": "GenreId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Genre primary key identifier"
|
||||
"description": "Genre primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the genre"
|
||||
"description": "The name of the genre",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Invoice"],
|
||||
"type": "table",
|
||||
"primary_key": ["InvoiceId"],
|
||||
"foreign_keys": {
|
||||
"InvoiceCustomer": {
|
||||
@ -285,60 +376,82 @@
|
||||
"name": "InvoiceId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Invoice primary key identifier"
|
||||
"description": "Invoice primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "CustomerId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "ID of the customer who bought the music"
|
||||
"description": "ID of the customer who bought the music",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "InvoiceDate",
|
||||
"type": "DateTime",
|
||||
"nullable": false,
|
||||
"description": "Date of the invoice"
|
||||
"description": "Date of the invoice",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BillingAddress",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The invoice's billing address line (street number, street)"
|
||||
"description": "The invoice's billing address line (street number, street)",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BillingCity",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The invoice's billing address city"
|
||||
"description": "The invoice's billing address city",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BillingState",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The invoice's billing address state"
|
||||
"description": "The invoice's billing address state",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BillingCountry",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The invoice's billing address country"
|
||||
"description": "The invoice's billing address country",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "BillingPostalCode",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The invoice's billing address postal code"
|
||||
"description": "The invoice's billing address postal code",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Total",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The total amount due on the invoice"
|
||||
"description": "The total amount due on the invoice",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["InvoiceLine"],
|
||||
"type": "table",
|
||||
"primary_key": ["InvoiceLineId"],
|
||||
"foreign_keys": {
|
||||
"Invoice": {
|
||||
@ -360,36 +473,50 @@
|
||||
"name": "InvoiceLineId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Invoice Line primary key identifier"
|
||||
"description": "Invoice Line primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "InvoiceId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "ID of the invoice the line belongs to"
|
||||
"description": "ID of the invoice the line belongs to",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "TrackId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "ID of the music track being purchased"
|
||||
"description": "ID of the music track being purchased",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "UnitPrice",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Price of each individual track unit"
|
||||
"description": "Price of each individual track unit",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Quantity",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Quantity of the track purchased"
|
||||
"description": "Quantity of the track purchased",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["MediaType"],
|
||||
"type": "table",
|
||||
"primary_key": ["MediaTypeId"],
|
||||
"description": "Collection of media types that tracks can be encoded in",
|
||||
"columns": [
|
||||
@ -397,18 +524,26 @@
|
||||
"name": "MediaTypeId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Media Type primary key identifier"
|
||||
"description": "Media Type primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the media type format"
|
||||
"description": "The name of the media type format",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Playlist"],
|
||||
"type": "table",
|
||||
"primary_key": ["PlaylistId"],
|
||||
"description": "Collection of playlists",
|
||||
"columns": [
|
||||
@ -416,18 +551,26 @@
|
||||
"name": "PlaylistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "Playlist primary key identifier"
|
||||
"description": "Playlist primary key identifier",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the playlist"
|
||||
"description": "The name of the playlist",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["PlaylistTrack"],
|
||||
"type": "table",
|
||||
"primary_key": ["PlaylistId", "TrackId"],
|
||||
"foreign_keys": {
|
||||
"Playlist": {
|
||||
@ -449,18 +592,26 @@
|
||||
"name": "PlaylistId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the playlist"
|
||||
"description": "The ID of the playlist",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "TrackId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the track"
|
||||
"description": "The ID of the track",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
},
|
||||
{
|
||||
"name": ["Track"],
|
||||
"type": "table",
|
||||
"primary_key": ["TrackId"],
|
||||
"foreign_keys": {
|
||||
"Album": {
|
||||
@ -488,56 +639,77 @@
|
||||
"name": "TrackId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the track"
|
||||
"description": "The ID of the track",
|
||||
"insertable": true,
|
||||
"updatable": false
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
"description": "The name of the track"
|
||||
"description": "The name of the track",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "AlbumId",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "The ID of the album the track belongs to"
|
||||
"description": "The ID of the album the track belongs to",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "MediaTypeId",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The ID of the media type the track is encoded with"
|
||||
"description": "The ID of the media type the track is encoded with",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "GenreId",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "The ID of the genre of the track"
|
||||
"description": "The ID of the genre of the track",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Composer",
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The name of the composer of the track"
|
||||
"description": "The name of the composer of the track",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Milliseconds",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The length of the track in milliseconds"
|
||||
"description": "The length of the track in milliseconds",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "Bytes",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "The size of the track in bytes"
|
||||
"description": "The size of the track in bytes",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
},
|
||||
{
|
||||
"name": "UnitPrice",
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
"description": "The price of the track"
|
||||
"description": "The price of the track",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
}
|
||||
]
|
||||
|
@ -9,11 +9,13 @@ import Control.Monad (forM_)
|
||||
import Control.Monad.Catch (MonadThrow)
|
||||
import Control.Monad.IO.Class (MonadIO)
|
||||
import Data.Aeson (Value (..), toJSON)
|
||||
import Data.Aeson.KeyMap qualified as KeyMap
|
||||
import Data.Aeson.Lens (_Object)
|
||||
import Data.Foldable (find)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List (sort, sortOn)
|
||||
import Data.List.NonEmpty qualified as NonEmpty
|
||||
import Data.Maybe (isJust)
|
||||
import Data.Text qualified as Text
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Test.AgentClient (AgentClientT, HasAgentClient, getSchemaGuarded)
|
||||
@ -28,6 +30,10 @@ import Prelude
|
||||
|
||||
spec :: TestData -> API.SourceName -> API.Config -> API.Capabilities -> AgentTestSpec
|
||||
spec TestData {..} sourceName config API.Capabilities {..} = describe "schema API" $ do
|
||||
let supportsInserts = isJust $ _cMutations >>= API._mcInsertCapabilities
|
||||
let supportsUpdates = isJust $ _cMutations >>= API._mcUpdateCapabilities
|
||||
let supportsDeletes = isJust $ _cMutations >>= API._mcDeleteCapabilities
|
||||
|
||||
it "returns the Chinook tables" $ do
|
||||
let extractTableNames = sort . fmap API._tiName
|
||||
tableNames <- (extractTableNames . API._srTables) <$> getSchemaGuarded sourceName config
|
||||
@ -36,7 +42,7 @@ spec TestData {..} sourceName config API.Capabilities {..} = describe "schema AP
|
||||
tableNames `jsonShouldBe` expectedTableNames
|
||||
|
||||
testPerTable "returns the correct columns in the Chinook tables" $ \expectedTable@API.TableInfo {..} -> do
|
||||
tables <- find (\t -> API._tiName t == _tiName) . API._srTables <$> getSchemaGuarded sourceName config
|
||||
actualTable <- find (\t -> API._tiName t == _tiName) . API._srTables <$> getSchemaGuarded sourceName config
|
||||
|
||||
-- We remove some properties here so that we don't compare them since they vary between agent implementations
|
||||
let extractJsonForComparison table =
|
||||
@ -46,16 +52,53 @@ spec TestData {..} sourceName config API.Capabilities {..} = describe "schema AP
|
||||
column
|
||||
& _Object . at "type" .~ Nothing -- Types can vary between agents since underlying datatypes can change
|
||||
& _Object . at "description" .~ Nothing -- Descriptions are not supported by all agents
|
||||
-- If the agent only supports nullable columns, we make all columns nullable
|
||||
let setExpectedColumnNullability columns =
|
||||
if API._dscColumnNullability _cDataSchema == API.OnlyNullableColumns
|
||||
then columns & traverse %~ (_Object . at "nullable" ?~ Bool True)
|
||||
else columns
|
||||
let actualJsonColumns = extractJsonForComparison <$> tables
|
||||
let expectedJsonColumns = Just . setExpectedColumnNullability $ extractJsonForComparison expectedTable
|
||||
let actualJsonColumns = extractJsonForComparison <$> actualTable
|
||||
let expectedJsonColumns =
|
||||
expectedTable
|
||||
& extractJsonForComparison
|
||||
-- If the agent only supports nullable columns, we make all columns nullable
|
||||
& applyWhen
|
||||
(API._dscColumnNullability _cDataSchema == API.OnlyNullableColumns)
|
||||
(traverse %~ (_Object . at "nullable" ?~ Bool True))
|
||||
-- If the agent doesn't support insert mutations then all columns should not be insertable
|
||||
& applyWhen
|
||||
(not supportsInserts)
|
||||
(traverse %~ (_Object . at "insertable" ?~ Bool False))
|
||||
-- If agent doesn't support update mutations then all columns should not be updatable
|
||||
& applyWhen
|
||||
(not supportsUpdates)
|
||||
(traverse %~ (_Object . at "updatable" ?~ Bool False))
|
||||
& Just
|
||||
|
||||
actualJsonColumns `jsonShouldBe` expectedJsonColumns
|
||||
|
||||
testPerTable "returns the correct mutability in the Chinook tables" $ \expectedTable@API.TableInfo {..} -> do
|
||||
actualTable <- find (\t -> API._tiName t == _tiName) . API._srTables <$> getSchemaGuarded sourceName config
|
||||
|
||||
let extractJsonForComparison (table :: API.TableInfo) =
|
||||
toJSON table
|
||||
& _Object %~ (KeyMap.filterWithKey (\prop _value -> prop `elem` ["insertable", "updatable", "deletable"]))
|
||||
|
||||
let actualComparisonJson = extractJsonForComparison <$> actualTable
|
||||
let expectedComparisonJson =
|
||||
expectedTable
|
||||
& extractJsonForComparison
|
||||
-- If the agent doesn't support insert mutations then the table should not be insertable
|
||||
& applyWhen
|
||||
(not supportsInserts)
|
||||
(_Object . at "insertable" ?~ Bool False)
|
||||
-- If the agent doesn't support update mutations then the table should not be updatable
|
||||
& applyWhen
|
||||
(not supportsUpdates)
|
||||
(_Object . at "updatable" ?~ Bool False)
|
||||
-- If the agent doesn't support delete mutations then the table should not be deletable
|
||||
& applyWhen
|
||||
(not supportsDeletes)
|
||||
(_Object . at "deletable" ?~ Bool False)
|
||||
& Just
|
||||
|
||||
actualComparisonJson `jsonShouldBe` expectedComparisonJson
|
||||
|
||||
if API._dscSupportsPrimaryKeys _cDataSchema
|
||||
then testPerTable "returns the correct primary keys for the Chinook tables" $ \API.TableInfo {..} -> do
|
||||
tables <- find (\t -> API._tiName t == _tiName) . API._srTables <$> getSchemaGuarded sourceName config
|
||||
@ -90,3 +133,7 @@ spec TestData {..} sourceName config API.Capabilities {..} = describe "schema AP
|
||||
forM_ _tdSchemaTables $ \expectedTable@API.TableInfo {..} -> do
|
||||
it (Text.unpack . NonEmpty.last $ API.unTableName _tiName) $
|
||||
test expectedTable
|
||||
|
||||
applyWhen :: Bool -> (a -> a) -> a -> a
|
||||
applyWhen True f x = f x
|
||||
applyWhen False _ x = x
|
||||
|
@ -96,316 +96,425 @@ schema =
|
||||
{ API._srTables =
|
||||
[ API.TableInfo
|
||||
{ API._tiName = mkTableName "Artist",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "ArtistId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Artist primary key identifier"
|
||||
API._ciDescription = Just "Artist primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Name",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The name of the artist"
|
||||
API._ciDescription = Just "The name of the artist",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "ArtistId"],
|
||||
API._tiDescription = Just "Collection of artists of music",
|
||||
API._tiForeignKeys = API.ForeignKeys mempty
|
||||
API._tiForeignKeys = API.ForeignKeys mempty,
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Album",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "AlbumId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Album primary key identifier"
|
||||
API._ciDescription = Just "Album primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Title",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The title of the album"
|
||||
API._ciDescription = Just "The title of the album",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "ArtistId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The ID of the artist that created the album"
|
||||
API._ciDescription = Just "The ID of the artist that created the album",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "AlbumId"],
|
||||
API._tiDescription = Just "Collection of music albums created by artists",
|
||||
API._tiForeignKeys =
|
||||
API.ForeignKeys $
|
||||
HashMap.singleton (API.ConstraintName "Artist") (API.Constraint (mkTableName "Artist") (HashMap.singleton (API.ColumnName "ArtistId") (API.ColumnName "ArtistId")))
|
||||
HashMap.singleton (API.ConstraintName "Artist") (API.Constraint (mkTableName "Artist") (HashMap.singleton (API.ColumnName "ArtistId") (API.ColumnName "ArtistId"))),
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Customer",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "CustomerId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Customer primary key identifier"
|
||||
API._ciDescription = Just "Customer primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "FirstName",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The customer's first name"
|
||||
API._ciDescription = Just "The customer's first name",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "LastName",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The customer's last name"
|
||||
API._ciDescription = Just "The customer's last name",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Company",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's company name"
|
||||
API._ciDescription = Just "The customer's company name",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Address",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's address line (street number, street)"
|
||||
API._ciDescription = Just "The customer's address line (street number, street)",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "City",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's address city"
|
||||
API._ciDescription = Just "The customer's address city",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "State",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's address state"
|
||||
API._ciDescription = Just "The customer's address state",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Country",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's address country"
|
||||
API._ciDescription = Just "The customer's address country",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "PostalCode",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's address postal code"
|
||||
API._ciDescription = Just "The customer's address postal code",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Phone",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's phone number"
|
||||
API._ciDescription = Just "The customer's phone number",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Fax",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The customer's fax number"
|
||||
API._ciDescription = Just "The customer's fax number",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Email",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The customer's email address"
|
||||
API._ciDescription = Just "The customer's email address",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "SupportRepId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The ID of the Employee who is this customer's support representative"
|
||||
API._ciDescription = Just "The ID of the Employee who is this customer's support representative",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "CustomerId"],
|
||||
API._tiDescription = Just "Collection of customers who can buy tracks",
|
||||
API._tiForeignKeys =
|
||||
API.ForeignKeys $
|
||||
HashMap.singleton (API.ConstraintName "CustomerSupportRep") (API.Constraint (mkTableName "Employee") (HashMap.singleton (API.ColumnName "SupportRepId") (API.ColumnName "EmployeeId")))
|
||||
HashMap.singleton (API.ConstraintName "CustomerSupportRep") (API.Constraint (mkTableName "Employee") (HashMap.singleton (API.ColumnName "SupportRepId") (API.ColumnName "EmployeeId"))),
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Employee",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "EmployeeId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Employee primary key identifier"
|
||||
API._ciDescription = Just "Employee primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "LastName",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The employee's last name"
|
||||
API._ciDescription = Just "The employee's last name",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "FirstName",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The employee's first name"
|
||||
API._ciDescription = Just "The employee's first name",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Title",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's job title"
|
||||
API._ciDescription = Just "The employee's job title",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "ReportsTo",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's report"
|
||||
API._ciDescription = Just "The employee's report",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BirthDate",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's birth date"
|
||||
API._ciDescription = Just "The employee's birth date",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "HireDate",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's hire date"
|
||||
API._ciDescription = Just "The employee's hire date",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Address",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's address line (street number, street)"
|
||||
API._ciDescription = Just "The employee's address line (street number, street)",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "City",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's address city"
|
||||
API._ciDescription = Just "The employee's address city",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "State",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's address state"
|
||||
API._ciDescription = Just "The employee's address state",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Country",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's address country"
|
||||
API._ciDescription = Just "The employee's address country",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "PostalCode",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's address postal code"
|
||||
API._ciDescription = Just "The employee's address postal code",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Phone",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's phone number"
|
||||
API._ciDescription = Just "The employee's phone number",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Fax",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's fax number"
|
||||
API._ciDescription = Just "The employee's fax number",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Email",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The employee's email address"
|
||||
API._ciDescription = Just "The employee's email address",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "EmployeeId"],
|
||||
API._tiDescription = Just "Collection of employees who work for the business",
|
||||
API._tiForeignKeys =
|
||||
API.ForeignKeys $
|
||||
HashMap.singleton (API.ConstraintName "EmployeeReportsTo") (API.Constraint (mkTableName "Employee") (HashMap.singleton (API.ColumnName "ReportsTo") (API.ColumnName "EmployeeId")))
|
||||
HashMap.singleton (API.ConstraintName "EmployeeReportsTo") (API.Constraint (mkTableName "Employee") (HashMap.singleton (API.ColumnName "ReportsTo") (API.ColumnName "EmployeeId"))),
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Genre",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "GenreId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Genre primary key identifier"
|
||||
API._ciDescription = Just "Genre primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Name",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The name of the genre"
|
||||
API._ciDescription = Just "The name of the genre",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "GenreId"],
|
||||
API._tiDescription = Just "Genres of music",
|
||||
API._tiForeignKeys = API.ForeignKeys mempty
|
||||
API._tiForeignKeys = API.ForeignKeys mempty,
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Invoice",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "InvoiceId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Invoice primary key identifier"
|
||||
API._ciDescription = Just "Invoice primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "CustomerId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "ID of the customer who bought the music"
|
||||
API._ciDescription = Just "ID of the customer who bought the music",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "InvoiceDate",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Date of the invoice"
|
||||
API._ciDescription = Just "Date of the invoice",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BillingAddress",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The invoice's billing address line (street number, street)"
|
||||
API._ciDescription = Just "The invoice's billing address line (street number, street)",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BillingCity",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The invoice's billing address city"
|
||||
API._ciDescription = Just "The invoice's billing address city",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BillingState",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The invoice's billing address state"
|
||||
API._ciDescription = Just "The invoice's billing address state",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BillingCountry",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The invoice's billing address country"
|
||||
API._ciDescription = Just "The invoice's billing address country",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "BillingPostalCode",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The invoice's billing address postal code"
|
||||
API._ciDescription = Just "The invoice's billing address postal code",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Total",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The total amount due on the invoice"
|
||||
API._ciDescription = Just "The total amount due on the invoice",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "InvoiceId"],
|
||||
@ -413,40 +522,54 @@ schema =
|
||||
API._tiForeignKeys =
|
||||
API.ForeignKeys $
|
||||
HashMap.singleton (API.ConstraintName "InvoiceCustomer") $
|
||||
API.Constraint (mkTableName "Customer") (HashMap.singleton (API.ColumnName "CustomerId") (API.ColumnName "CustomerId"))
|
||||
API.Constraint (mkTableName "Customer") (HashMap.singleton (API.ColumnName "CustomerId") (API.ColumnName "CustomerId")),
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "InvoiceLine",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "InvoiceLineId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Invoice Line primary key identifier"
|
||||
API._ciDescription = Just "Invoice Line primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "InvoiceId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "ID of the invoice the line belongs to"
|
||||
API._ciDescription = Just "ID of the invoice the line belongs to",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "TrackId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "ID of the music track being purchased"
|
||||
API._ciDescription = Just "ID of the music track being purchased",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "UnitPrice",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Price of each individual track unit"
|
||||
API._ciDescription = Just "Price of each individual track unit",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Quantity",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Quantity of the track purchased"
|
||||
API._ciDescription = Just "Quantity of the track purchased",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "InvoiceLineId"],
|
||||
@ -456,84 +579,114 @@ schema =
|
||||
HashMap.fromList
|
||||
[ (API.ConstraintName "Invoice", API.Constraint (mkTableName "Invoice") (HashMap.singleton (API.ColumnName "InvoiceId") (API.ColumnName "InvoiceId"))),
|
||||
(API.ConstraintName "Track", API.Constraint (mkTableName "Track") (HashMap.singleton (API.ColumnName "TrackId") (API.ColumnName "TrackId")))
|
||||
]
|
||||
],
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "MediaType",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "MediaTypeId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "Media Type primary key identifier"
|
||||
API._ciDescription = Just "Media Type primary key identifier",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Name",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The name of the media type format"
|
||||
API._ciDescription = Just "The name of the media type format",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "MediaTypeId"],
|
||||
API._tiDescription = Just "Collection of media types that tracks can be encoded in",
|
||||
API._tiForeignKeys = API.ForeignKeys mempty
|
||||
API._tiForeignKeys = API.ForeignKeys mempty,
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "Track",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "TrackId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The ID of the track"
|
||||
API._ciDescription = Just "The ID of the track",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = False
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Name",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The name of the track"
|
||||
API._ciDescription = Just "The name of the track",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "AlbumId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The ID of the album the track belongs to"
|
||||
API._ciDescription = Just "The ID of the album the track belongs to",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "MediaTypeId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The ID of the media type the track is encoded with"
|
||||
API._ciDescription = Just "The ID of the media type the track is encoded with",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "GenreId",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The ID of the genre of the track"
|
||||
API._ciDescription = Just "The ID of the genre of the track",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Composer",
|
||||
API._ciType = API.StringTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The name of the composer of the track"
|
||||
API._ciDescription = Just "The name of the composer of the track",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Milliseconds",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The length of the track in milliseconds"
|
||||
API._ciDescription = Just "The length of the track in milliseconds",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "Bytes",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = True,
|
||||
API._ciDescription = Just "The size of the track in bytes"
|
||||
API._ciDescription = Just "The size of the track in bytes",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
},
|
||||
API.ColumnInfo
|
||||
{ API._ciName = API.ColumnName "UnitPrice",
|
||||
API._ciType = API.NumberTy,
|
||||
API._ciNullable = False,
|
||||
API._ciDescription = Just "The price of the track"
|
||||
API._ciDescription = Just "The price of the track",
|
||||
API._ciInsertable = True,
|
||||
API._ciUpdatable = True
|
||||
}
|
||||
],
|
||||
API._tiPrimaryKey = [API.ColumnName "TrackId"],
|
||||
@ -544,21 +697,28 @@ schema =
|
||||
[ (API.ConstraintName "Album", API.Constraint (mkTableName "Album") (HashMap.singleton (API.ColumnName "AlbumId") (API.ColumnName "AlbumId"))),
|
||||
(API.ConstraintName "Genre", API.Constraint (mkTableName "Genre") (HashMap.singleton (API.ColumnName "GenreId") (API.ColumnName "GenreId"))),
|
||||
(API.ConstraintName "MediaType", API.Constraint (mkTableName "MediaType") (HashMap.singleton (API.ColumnName "MediaTypeId") (API.ColumnName "MediaTypeId")))
|
||||
]
|
||||
],
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
},
|
||||
API.TableInfo
|
||||
{ API._tiName = mkTableName "MyCustomScalarsTable",
|
||||
API._tiType = API.Table,
|
||||
API._tiColumns =
|
||||
[ API.ColumnInfo (API.ColumnName "MyIntColumn") (API.CustomTy "MyInt") False Nothing,
|
||||
API.ColumnInfo (API.ColumnName "MyFloatColumn") (API.CustomTy "MyFloat") False Nothing,
|
||||
API.ColumnInfo (API.ColumnName "MyStringColumn") (API.CustomTy "MyString") False Nothing,
|
||||
API.ColumnInfo (API.ColumnName "MyBooleanColumn") (API.CustomTy "MyBoolean") False Nothing,
|
||||
API.ColumnInfo (API.ColumnName "MyIDColumn") (API.CustomTy "MyID") False Nothing,
|
||||
API.ColumnInfo (API.ColumnName "MyAnythingColumn") (API.CustomTy "MyAnything") False Nothing
|
||||
[ API.ColumnInfo (API.ColumnName "MyIntColumn") (API.CustomTy "MyInt") False Nothing True True,
|
||||
API.ColumnInfo (API.ColumnName "MyFloatColumn") (API.CustomTy "MyFloat") False Nothing True True,
|
||||
API.ColumnInfo (API.ColumnName "MyStringColumn") (API.CustomTy "MyString") False Nothing True True,
|
||||
API.ColumnInfo (API.ColumnName "MyBooleanColumn") (API.CustomTy "MyBoolean") False Nothing True True,
|
||||
API.ColumnInfo (API.ColumnName "MyIDColumn") (API.CustomTy "MyID") False Nothing True True,
|
||||
API.ColumnInfo (API.ColumnName "MyAnythingColumn") (API.CustomTy "MyAnything") False Nothing True True
|
||||
],
|
||||
API._tiPrimaryKey = [],
|
||||
API._tiDescription = Nothing,
|
||||
API._tiForeignKeys = API.ForeignKeys mempty
|
||||
API._tiForeignKeys = API.ForeignKeys mempty,
|
||||
API._tiInsertable = True,
|
||||
API._tiUpdatable = True,
|
||||
API._tiDeletable = True
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -183,30 +183,32 @@ resolveDatabaseMetadata' ::
|
||||
SourceTypeCustomization ->
|
||||
m (Either QErr (ResolvedSource 'DataConnector))
|
||||
resolveDatabaseMetadata' _ sc@DC.SourceConfig {_scSchema = API.SchemaResponse {..}, ..} customization =
|
||||
-- We need agents to provide the foreign key contraints inside 'API.SchemaResponse'
|
||||
let foreignKeys = fmap API._tiForeignKeys _srTables
|
||||
tables = Map.fromList $ do
|
||||
API.TableInfo {..} <- _srTables
|
||||
let primaryKeyColumns = Seq.fromList $ coerce <$> _tiPrimaryKey
|
||||
let meta =
|
||||
RQL.T.T.DBTableMetadata
|
||||
{ _ptmiOid = OID 0,
|
||||
{ _ptmiOid = OID 0, -- TODO: This is wrong and needs to be fixed. It is used for diffing tables and seeing what's new/deleted/altered, so reusing 0 for all tables is problematic.
|
||||
_ptmiColumns = do
|
||||
API.ColumnInfo {..} <- _tiColumns
|
||||
pure $
|
||||
RQL.T.C.RawColumnInfo
|
||||
{ rciName = Witch.from _ciName,
|
||||
rciPosition = 1,
|
||||
rciPosition = 1, -- TODO: This is very wrong and needs to be fixed. It is used for diffing tables and seeing what's new/deleted/altered, so reusing 1 for all columns is problematic.
|
||||
rciType = DC.mkScalarType _scCapabilities _ciType,
|
||||
rciIsNullable = _ciNullable,
|
||||
rciDescription = fmap GQL.Description _ciDescription,
|
||||
-- TODO: Add Column Mutability to the 'TableInfo'
|
||||
rciMutability = RQL.T.C.ColumnMutability False False
|
||||
rciMutability = RQL.T.C.ColumnMutability _ciInsertable _ciUpdatable
|
||||
},
|
||||
_ptmiPrimaryKey = RQL.T.T.PrimaryKey (RQL.T.T.Constraint (DC.ConstraintName "") (OID 0)) <$> NESeq.nonEmptySeq primaryKeyColumns,
|
||||
_ptmiUniqueConstraints = mempty,
|
||||
_ptmiForeignKeys = buildForeignKeySet foreignKeys,
|
||||
_ptmiViewInfo = Just $ RQL.T.T.ViewInfo False False False,
|
||||
_ptmiViewInfo =
|
||||
( if _tiType == API.Table && _tiInsertable && _tiUpdatable && _tiDeletable
|
||||
then Nothing
|
||||
else Just $ RQL.T.T.ViewInfo _tiInsertable _tiUpdatable _tiDeletable
|
||||
),
|
||||
_ptmiDescription = fmap PGDescription _tiDescription,
|
||||
_ptmiExtraTableMetadata = ()
|
||||
}
|
||||
|
@ -19,23 +19,25 @@ spec = do
|
||||
testToFromJSONToSchema (ColumnName "my_column_name") [aesonQQ|"my_column_name"|]
|
||||
jsonOpenApiProperties genColumnName
|
||||
describe "ColumnInfo" $ do
|
||||
describe "without description" $
|
||||
testToFromJSONToSchema
|
||||
(ColumnInfo (ColumnName "my_column_name") StringTy False Nothing)
|
||||
describe "minimal" $
|
||||
testFromJSON
|
||||
(ColumnInfo (ColumnName "my_column_name") StringTy False Nothing False False)
|
||||
[aesonQQ|
|
||||
{ "name": "my_column_name",
|
||||
"type": "string",
|
||||
"nullable": false
|
||||
}
|
||||
|]
|
||||
describe "with description" $
|
||||
describe "non-minimal" $
|
||||
testToFromJSONToSchema
|
||||
(ColumnInfo (ColumnName "my_column_name") NumberTy True (Just "My column description"))
|
||||
(ColumnInfo (ColumnName "my_column_name") NumberTy True (Just "My column description") True True)
|
||||
[aesonQQ|
|
||||
{ "name": "my_column_name",
|
||||
"type": "number",
|
||||
"nullable": true,
|
||||
"description": "My column description"
|
||||
"description": "My column description",
|
||||
"insertable": true,
|
||||
"updatable": true
|
||||
}
|
||||
|]
|
||||
jsonOpenApiProperties genColumnInfo
|
||||
@ -50,3 +52,5 @@ genColumnInfo =
|
||||
<*> genScalarType
|
||||
<*> Gen.bool
|
||||
<*> Gen.maybe (genArbitraryAlphaNumText defaultRange)
|
||||
<*> Gen.bool
|
||||
<*> Gen.bool
|
||||
|
@ -22,8 +22,8 @@ spec = do
|
||||
jsonOpenApiProperties genTableName
|
||||
describe "TableInfo" $ do
|
||||
describe "minimal" $
|
||||
testToFromJSONToSchema
|
||||
(TableInfo (TableName ["my_table_name"]) [] [] (ForeignKeys mempty) Nothing)
|
||||
testFromJSON
|
||||
(TableInfo (TableName ["my_table_name"]) Table [] [] (ForeignKeys mempty) Nothing False False False)
|
||||
[aesonQQ|
|
||||
{ "name": ["my_table_name"],
|
||||
"columns": []
|
||||
@ -33,30 +33,43 @@ spec = do
|
||||
testToFromJSONToSchema
|
||||
( TableInfo
|
||||
(TableName ["my_table_name"])
|
||||
[ColumnInfo (ColumnName "id") StringTy False Nothing]
|
||||
View
|
||||
[ColumnInfo (ColumnName "id") StringTy False Nothing False False]
|
||||
[ColumnName "id"]
|
||||
(ForeignKeys mempty)
|
||||
(Just "my description")
|
||||
True
|
||||
True
|
||||
True
|
||||
)
|
||||
[aesonQQ|
|
||||
{ "name": ["my_table_name"],
|
||||
"columns": [{"name": "id", "type": "string", "nullable": false}],
|
||||
"type": "view",
|
||||
"columns": [{"name": "id", "type": "string", "nullable": false, "insertable": false, "updatable": false}],
|
||||
"primary_key": ["id"],
|
||||
"description": "my description"
|
||||
"description": "my description",
|
||||
"insertable": true,
|
||||
"updatable": true,
|
||||
"deletable": true
|
||||
}
|
||||
|]
|
||||
describe "foreign-key" $
|
||||
testToFromJSONToSchema
|
||||
( TableInfo
|
||||
(TableName ["my_table_name"])
|
||||
[ColumnInfo (ColumnName "id") StringTy False Nothing]
|
||||
Table
|
||||
[ColumnInfo (ColumnName "id") StringTy False Nothing False False]
|
||||
[ColumnName "id"]
|
||||
(ForeignKeys $ HashMap.singleton (ConstraintName "Artist") (Constraint (TableName ["artist_table"]) (HashMap.singleton (ColumnName "ArtistId") (ColumnName "ArtistId"))))
|
||||
(Just "my description")
|
||||
False
|
||||
False
|
||||
False
|
||||
)
|
||||
[aesonQQ|
|
||||
{ "name": ["my_table_name"],
|
||||
"columns": [{"name": "id", "type": "string", "nullable": false}],
|
||||
"type": "table",
|
||||
"columns": [{"name": "id", "type": "string", "nullable": false, "insertable": false, "updatable": false}],
|
||||
"primary_key": ["id"],
|
||||
"description": "my description",
|
||||
"foreign_keys": {
|
||||
@ -66,7 +79,10 @@ spec = do
|
||||
"ArtistId": "ArtistId"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"insertable": false,
|
||||
"updatable": false,
|
||||
"deletable": false
|
||||
}
|
||||
|]
|
||||
jsonOpenApiProperties genTableInfo
|
||||
@ -85,12 +101,19 @@ genConstraint =
|
||||
let mapping = genHashMap genColumnName genColumnName defaultRange
|
||||
in Constraint <$> genTableName <*> mapping
|
||||
|
||||
genTableType :: MonadGen m => m TableType
|
||||
genTableType = Gen.enumBounded
|
||||
|
||||
-- | Note: this generator is intended for serialization tests only and does not ensure valid Foreign Key Constraints.
|
||||
genTableInfo :: (MonadGen m, GenBase m ~ Identity) => m TableInfo
|
||||
genTableInfo =
|
||||
TableInfo
|
||||
<$> genTableName
|
||||
<*> genTableType
|
||||
<*> Gen.list defaultRange genColumnInfo
|
||||
<*> Gen.list defaultRange genColumnName
|
||||
<*> genForeignKeys
|
||||
<*> Gen.maybe (genArbitraryAlphaNumText defaultRange)
|
||||
<*> Gen.bool
|
||||
<*> Gen.bool
|
||||
<*> Gen.bool
|
||||
|
@ -1,6 +1,7 @@
|
||||
-- | Some helper functions for testing Aeson instances
|
||||
module Test.Aeson.Utils
|
||||
( testToFromJSON,
|
||||
( testFromJSON,
|
||||
testToFromJSON,
|
||||
validateToJSONOpenApi,
|
||||
testToFromJSONToSchema,
|
||||
jsonRoundTrip,
|
||||
@ -26,10 +27,14 @@ import Hedgehog.Internal.Range
|
||||
import Test.Hspec
|
||||
import Test.Hspec.Hedgehog
|
||||
|
||||
testToFromJSON :: (HasCallStack, Eq a, Show a, FromJSON a, ToJSON a) => a -> Value -> Spec
|
||||
testToFromJSON a v = do
|
||||
testFromJSON :: (HasCallStack, Eq a, Show a, FromJSON a) => a -> Value -> Spec
|
||||
testFromJSON a v = do
|
||||
it "parses from JSON" $
|
||||
parseEither parseJSON v `shouldBe` Right a
|
||||
|
||||
testToFromJSON :: (HasCallStack, Eq a, Show a, FromJSON a, ToJSON a) => a -> Value -> Spec
|
||||
testToFromJSON a v = do
|
||||
testFromJSON a v
|
||||
it "encodes to JSON" $
|
||||
toJSON a `shouldBe` v
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user