### Description * Removed unresolved questions since resolved * Change status 'draft' → 'published' ### Changelog - [X] `no-changelog-required` . ### Related Issues fix https://github.com/hasura/graphql-engine/issues/7405 PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2496 GitOrigin-RevId: 0bdba7f5d52f2a4bd70c525dab12698e9ceddb28
8.3 KiB
Customising Function Root Field Names
Metadata
---
authors:
Vamshi Surabhi <vamshi@hasura.io>,
Gil Mizrahi <gil@hasura.io,
Evie Ciobanu <evie@hasura.io>,
Philip Lykke Carlsen <philip@hasura.io>
discussion:
https://github.com/hasura/graphql-engine/issues/7405
state: published
---
Description
We should let users customise the GraphQL name that appears in the schema of SQL Functions tracked by The GraphQL Engine.
Problem
Being able to customise the names that tables are exposed under in the GraphQL schema is already supported by The GraphQL Engine, since PR-5719.
Citing the above PR for motivation:
.. The identifier can be useful to use when tracking tables with non GraphQL compliant names or it can be useful if the nodes that are generated should not be dependent on the table name i.e when a table is tracked with an identifier and if the table has been renamed, the node names will not be changed due to the identifier.
The same motivation applies for functions that are exposed in the GraphQL Schema.
Success
This feature will have been implemented fully when a user is able to, through the Console:
- Track a function
- Set a custom name for it
- Use that name in queries
Similarly, the server-specific part will have been implemented once there is an integration test that performs the above steps via the server API.
What
The server part should add new metadata API endpoint(s) that allows a user, for some given tracked function, to specify a custom name that is used to query this function in the GraphQL Schema. In the absence of this feature, the name of the tracked function is just directly exposed in the GraphQL schema [1].
The console part should include a tab within the tracked function page that lets users specify the customised name, similarly to how custom table names are specified.
It's important to note that this change only affects the GraphQL frontend, leaving SQL generation untouched.
Also note that, since the Postgres backend is the only one that supports functions, the change described in this document is only in scope for Postgres. In the longer perspective, the query abstraction feature that will be implemented across backends will not be SQL Functions directly, but rather Stored Queries, which as of this writing have yet to be specified. (See Slack Discussion)
Effects and Interactions
This will require work on both Server and Console. Once the Server parts have been implemented the Console parts can be implemented.
This adds new metadata API endpoints. We don't perceive this feature to interfere with any other features.
Unresolved Questions
None. See linked discussion for historically unresolved questions.
How
Functions already have a configuration
section - we can add two top level
fields called custom_name
and custom_root_fields
as follows:
functions:
- function: function_name
configuration:
session_argument: ...
exposed_as: ...
custom_name: a custom graphql compliant name
custom_root_fields:
function: FunctionName
function_aggregate: SomeOtherName
permissions: ...
Console will require an equivalent of set_table_customization
, maybe
set_function_configuration
to allow modifying this configuration after a
function is tracked.
The configuration
section of a function (FunctionMetadata b
) needs two new
options:
-
custom_name
is optional and expects a GraphQL name.custom_name
should be used in place of the function's name when generating GraphQL fields or types for the function. -
custom_root_fields
is optional and expects an object with the following two fields (both should be GraphQL names):-
function
: This name should be used for the field name which exposes the function in the appropriate operation root (query/mutation/subscription). When this not specified, we use<custom_name/function_name>
as the field name. -
function_aggregate
: This name should be used for the aggregate operation that is exposed on the function. In the absence of this option, we use<custom_name/function_name>_aggregate
as the field name.
Note that both
function
andfunction_name
take precedence overcustom_name
. -
We do not expect functions support on other databases, so this is only expected to work on Postgres databases.
(These are equivalent to custom_name
and custom_root_fields
in a table's
configuration
section (TableConfig b
))
In addition, we need an API for updating this configuration from the console.
For table configuration, it is done through set_table_customization
so we need
something similar to that for functions too. I'm wondering if it makes sense to
just add replace_if_exists
key to track_function
API instead of a
set_function_customization
API.
Examples
Example, consider the following schema:
create table article (
id serial primary key,
title text not null,
content text not null
);
create function search_articles(key text)
returns setof articles AS $$
select * from article
where
title ilike ('%' || key || '%')
OR content ilike ('%' || key || '%')
$$ language sql stable;
with this metadata:
version: 3
sources:
- name: wb
kind: postgres
configuration:
connection_info:
database_url:
from_env: WORKBENCH_URL
tables:
- table: article
functions:
- function: search_articles
We would have the following fields in query_root
(considering only the fields
of function search_articles
):
search_articles(
args: search_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): [article!]!
search_articles_aggregate(
args: search_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): article_aggregate!
If custom_name
is specified as follows:
version: 3
sources:
- name: wb
kind: postgres
configuration:
connection_info:
database_url:
from_env: WORKBENCH_URL
tables:
- table: article
functions:
- function: search_articles
configuration:
custom_name: find_articles
the root fields would have the following schema:
find_articles(
args: find_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): [article!]!
find_articles_aggregate(
args: find_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): article_aggregate!
The changes are:
search_articles -> find_articles (root field name)
search_articles_aggregate -> find_articles_aggregate (root field name)
search_articles_args -> find_articles_args (input type name)
Instead, if custom_root_fields
are specified as follows:
version: 3
sources:
- name: wb
kind: postgres
configuration:
connection_info:
database_url:
from_env: WORKBENCH_URL
tables:
- table: article
functions:
- function: search_articles
configuration:
custom_name: find_articles
custom_root_fields:
function: FindArticles
function_aggregate: FindArticlesAggregate
the generated schema should be:
FindArticles(
args: find_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): [article!]!
FindArticlesAggregate(
args: find_articles_args!
distinct_on: [article_select_column!]
limit: Int
offset: Int
order_by: [article_order_by!]
where: article_bool_exp
): article_aggregate!
The changes are:
find_articles -> FindArticles (root field name)
find_articles_aggregate -> FindArticlesAggregate (root field name)
Future work
In general maybe we can extend most of our
track_*/create_*
APIs with replace_if_exists
similar to CREATE OR REPLACE
in Postgres?
Footnotes
[1]^: Subject to the name being made into a valid GraphQL identifier.