Skip to content

Commit 308be20

Browse files
committed
Merge branch 'main' into pre-pg-proc-refacting
2 parents 2a60577 + 5565776 commit 308be20

File tree

12 files changed

+845
-111
lines changed

12 files changed

+845
-111
lines changed

docs/internals/StoredProcedureDesignDoc.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Design/Spec Summary
44
### Entity Source Object Config
5-
Currently, the `source` attribute is always a string. With the addition of stored procedure support and the next version of the JSON schema: [here](https://github.com/Azure/project-hawaii/blob/db619b4175719c83d540bc30ef5acc5faa6faa6d/playground/hawaii.draft-02.schema.json), the `source` attribute is now optionally an object with attributes like so:
5+
Currently, the `source` attribute is always a string. With the addition of stored procedure support and the next version of the JSON schema: [here](https://dataapibuilder.azureedge.net/schemas/v0.4.11-alpha/dab.draft.schema.json), the `source` attribute is now optionally an object with attributes like so:
66
```json
77
"type": {
88
"type": "string",
@@ -51,21 +51,23 @@ parameters can either be fixed as above or passed at runtime through
5151
5252
### Stored Procedure Permissions
5353

54-
Stored procedures have identical role/action permissions to any other entity. i.e. same familiar format:
54+
Stored procedure backed entities have similar role/action permissions to other entities. i.e. same familiar format:
55+
5556
```json
5657
"permissions": [
5758
{
5859
"role": "anonymous",
59-
"actions": [ "read" ]
60+
"actions": [ "execute" ]
6061

6162
},
6263
{
6364
"role": "authenticated",
64-
"actions": [ "create" ]
65+
"actions": [ "execute" ]
6566
}
6667
]
67-
```
68-
However, the behavior of **column/field-level permissions** and **database policies** have not yet been designed/defined for procedures; as such, these will be **ignored**.
68+
```
69+
70+
However, stored procedures only accept the action **execute**. **Column/field-level permissions** and **database policies** are not supported.
6971

7072
<a id='suggested-config'></a>
7173
> Why not simplify stored procedure permissions, if POST, PUT, PATCH, DELETE semantically identical?
@@ -75,6 +77,7 @@ Justification **against** supporting all CRUD operations:
7577
- Other solutions usually limit to `POST`: https://learn.microsoft.com/rest/api/cosmos-db/execute-a-stored-procedure
7678
- [PostgREST](https://postgrest.org/en/stable/api.html#stored-procedures) support `POST` and `GET` if marked `IMMUTABLE` or `STABLE`
7779
- Proposed permission:
80+
7881
```json
7982
"permissions": [
8083
{
@@ -87,11 +90,13 @@ Justification **against** supporting all CRUD operations:
8790
}
8891
]
8992
```
93+
9094
Justification **for** allowing permission configuration for all CRUD operations:
95+
9196
- Davide: the "simplification" would complicate authorization with no real benefit. True in that the authorization logic would need to change conditioned on whether the entity source was a stored procedure.
92-
- Aniruddh: we should leave the responsibility for the developer to properly configure hawaii; it's not our fault if they configure against the API guidelines
97+
- Aniruddh: we should leave the responsibility for the developer to properly configure Data API Builder; it's not our fault if they configure against the API guidelines
9398

94-
Users can provide only one CRUD operation for stored-procedure.
99+
Users can provide only one CRUD operation for stored-procedure.
95100
CREATE/UPDATE/DELETE(CUD) action will create mutation operation, while READ will create a Query operation for GraphQL.
96101
Providing more than one (CRUD) operation would throw an error and the engine will fail to start during initialization.
97102

docs/views-and-stored-procedures.md

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Stored procedures can be used as objects related to entities exposed by Data API
4848
If you have a stored procedure, for example [`dbo.stp_get_all_cowritten_books_by_author`](../samples/getting-started/azure-sql-db/library.azure-sql.sql#L138) it can be exposed using the following `dab` command:
4949

5050
```sh
51-
dab add GetCowrittenBooksByAuthor --source dbo.stp_get_all_cowritten_books_by_author --source.type "stored-procedure" source.params "searchType:s" --permissions "anonymous:read" --rest true --graphql true
51+
dab add GetCowrittenBooksByAuthor --source dbo.stp_get_all_cowritten_books_by_author --source.type "stored-procedure" source.params "searchType:s" --permissions "anonymous:execute" --rest.methods "get" --graphql.operation "query"
5252
```
5353

5454
the `dab-config.json` file will look like the following:
@@ -62,47 +62,127 @@ the `dab-config.json` file will look like the following:
6262
"searchType": "s"
6363
}
6464
},
65-
"rest": true,
66-
"graphql": true,
65+
"rest": {
66+
"methods": [ "GET" ]
67+
},
68+
"graphql": {
69+
"operation": "query"
70+
},
6771
"permissions": [{
6872
"role": "anonymous",
69-
"actions": [ "read" ]
73+
"actions": [ "execute" ]
7074
}]
7175
}
7276
```
7377

7478
The `parameters` defines which parameters should be exposed and to provide default values to be passed to the stored procedure parameters, if those are not provided in the HTTP request.
7579

76-
**ATTENTION**:
80+
**Limitations**:
7781

7882
1. Only the first result set returned by the stored procedure will be used by Data API Builder.
79-
1. If more than one CRUD action is specified in the config, runtime initialization will fail due to config validation error.
80-
81-
Please note that **you should configure the permission accordingly with the stored procedure behavior**. For example, if a Stored Procedure create a new item in the database, it is recommended to allow only the action `create` for such stored procedure. If, like in the sample, a stored procedure returns some data, it is recommended to allow only the action `read`. In general the recommendation is to align the allowed actions to what the stored procedure does, so to provide a consistent experience to the developer.
83+
2. For both REST and GraphQL endpoints: when a stored procedure parameter is specified both in the configuration file and in the URL query string, the parameter in the URL query string will take precedence.
84+
3. Entities backed by a stored procedure do not have all the capabilities automatically provided for entities backed by tables, collections or views. Stored procedure backed entities do not support pagination, ordering, or filtering. Nor do such entities support returning items specified by primary key values.
8285

8386
### REST support for stored procedures
8487

85-
Entities backed by a stored procedure, do not have all the capabilities automatically provided for entities backed by tables, collections or views. An entity using a stored procedure will not have support for pagination, ordering, filtering or for returning an item by specifying the primary key values.
88+
The REST endpoint behavior for a stored procedure backed entity can be configured to be available for one or multiple HTTP verbs (GET, POST, PUT, PATCH, DELETE). The REST section of the entity would look like the following:
89+
90+
```json
91+
"rest": {
92+
"methods": [ "GET", "POST" ]
93+
}
94+
```
95+
96+
Any REST requests for the entity will fail with **HTTP 405 Method Not Allowed** when an HTTP method not listed in the configuration is used. e.g. executing a PUT request will fail with error code 405.
97+
If the `methods` section is excluded from the entity's REST configuration, the default method **POST** will be inferred. To disable the REST endpoint for this entity, configure `"rest": false` and any REST requests on the stored procedure entity will fail with **HTTP 404 Not Found**.
8698

8799
If the stored procedure accepts parameters, those can be passed in the URL query string when calling the REST endpoint. For example:
88100

89101
```text
90102
http://<dab-server>/api/GetCowrittenBooksByAuthor?author=isaac%20asimov
91103
```
92104

93-
If a parameter is specified both in the configuration file and in the URL query string, the one in the URL query string will take precedence.
94-
95105
### GraphQL support for stored procedures
96106

97-
Just like for REST, entities backed by a stored procedure, do not have all the capabilities automatically provided for entities backed by tables, collections or views. An entity using a stored procedure will not have support for pagination, ordering, filtering or for returning an item by specifying the primary key values.
107+
Stored procedure execution in GraphQL can be configured using the `graphql` option of a stored procedure backed entity. Explicitly setting the operation of the entity allows you to represent a stored procedure in the GraphQL schema in a way that aligns with the behavior of the stored procedure.
108+
Not setting any value for the operation will result in the creation of a `mutation` operation.
109+
110+
For example, using the value `query` for the `operation` option results in the stored procedure resolving as a query field in the GraphQL schema
111+
112+
CLI Usage:
113+
114+
```sh
115+
dab add GetCowrittenBooksByAuthor --source dbo.stp_get_all_cowritten_books_by_author --source.type "stored-procedure" --source.params "searchType:s" --permissions "anonymous:execute" --rest.methods "GET" --graphql.operation "query"
116+
```
117+
118+
Runtime Configuration:
119+
120+
```json
121+
"graphql": {
122+
"operation": "query"
123+
}
124+
```
125+
126+
GraphQL Schema Components: type and query field:
127+
128+
```graphql
129+
type GetCowrittenBooksByAuthor {
130+
id: Int!
131+
title: String
132+
}
133+
134+
In the schema, both query and mutation operations for stored procedures will have `execute` as a prefix. For the above stored procedure the exact query name field generated would be `executeGetCowrittenBooksByAuthor`
135+
136+
type Query {
137+
"""
138+
Execute Stored-Procedure GetCowrittenBooksByAuthor and get results from the database
139+
"""
140+
executeGetCowrittenBooksByAuthor(
141+
"""
142+
parameters for GetCowrittenBooksByAuthor stored-procedure
143+
"""
144+
searchType: String = "S"
145+
): [GetCowrittenBooksByAuthor!]!
146+
}
147+
```
148+
149+
Alternatively, `operation` can be set to `mutation` so that a mutation field represents the stored procedure in the GraphQL schema. The below `dab update` command can be used to change the `operation`:
150+
151+
```sh
152+
dab update GetCowrittenBooksByAuthor --graphql.operation "mutation"
153+
```
154+
155+
Runtime configuration:
156+
157+
```json
158+
"graphql": {
159+
"operation": "mutation"
160+
}
161+
```
162+
GraphQL Schema Components: type and mutation field:
163+
164+
type Mutation {
165+
type GetCowrittenBooksByAuthor {
166+
id: Int!
167+
title: String
168+
}
98169

99-
Depending on the `action` defined in the configuration file a GraphQL query object will be generated - if `read` action has been specified - or a mutation object will be created - if `create`, `update` or `delete` action has been specified.
170+
"""
171+
Execute Stored-Procedure GetCowrittenBooksByAuthor and get results from the database
172+
"""
173+
executeGetCowrittenBooksByAuthor(
174+
"""
175+
parameters for GetCowrittenBooksByAuthor stored-procedure
176+
"""
177+
searchType: String = "S"
178+
): [GetCowrittenBooksByAuthor!]!
179+
}
100180

101181
If the stored procedure accepts parameters, those can be passed as parameter of the query or mutation. For example:
102182

103183
```graphql
104184
query {
105-
GetCowrittenBooksByAuthor(author:"asimov")
185+
executeGetCowrittenBooksByAuthor(author:"asimov")
106186
{
107187
id
108188
title
@@ -111,5 +191,3 @@ query {
111191
}
112192
}
113193
```
114-
115-
If a parameter is specified both in the configuration file and in the URL query string for a stored procedure, the one in the URL query string will take precedence.

src/Cli/src/ConfigGenerator.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,6 @@ private static bool TryAddGraphQLOperationForDatabaseExecutable(EntityOptions op
10091009
/// RestDatabaseExecutableEntityVerboseSettings-> when a stored procedure entity/function is configured with explicit RestMethods and Path settings.</returns>
10101010
private static object? ConstructUpdatedRestDetails(Entity entity, EntityOptions options)
10111011
{
1012-
10131012
// Updated REST Route details
10141013
object? restPath = (options.RestRoute is not null) ? ConstructRestPathDetails(options.RestRoute) : entity.GetRestEnabledOrPathSettings();
10151014

@@ -1106,7 +1105,11 @@ private static bool TryAddGraphQLOperationForDatabaseExecutable(EntityOptions op
11061105
}
11071106
else
11081107
{
1109-
if (options.GraphQLOperationForDatabaseExecutable is not null)
1108+
if (options.GraphQLOperationForDatabaseExecutable is null)
1109+
{
1110+
graphQLOperation = null;
1111+
}
1112+
else
11101113
{
11111114
graphQLType = null;
11121115
}

src/Cli/src/Utils.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ public static string GetProductVersion()
9393
// definitions and with/without a custom graphQL type definition.
9494
else if (graphQLDetail is not null && graphQLOperation is null)
9595
{
96-
if (graphQLDetail is true || graphQLDetail is false)
96+
if (graphQLDetail is bool graphQLEnabled)
9797
{
98-
return graphQLDetail;
98+
return graphQLEnabled;
9999
}
100100
else
101101
{
@@ -1006,14 +1006,13 @@ public static bool CheckConflictingGraphQLConfigurationForDatabaseExecutable(Ent
10061006
public static object? ConstructGraphQLTypeDetails(string? graphQL)
10071007
{
10081008
object? graphQLType;
1009-
bool graphQLEnabled;
10101009
if (graphQL is null)
10111010
{
10121011
graphQLType = null;
10131012
}
10141013
else
10151014
{
1016-
if (bool.TryParse(graphQL, out graphQLEnabled))
1015+
if (bool.TryParse(graphQL, out bool graphQLEnabled))
10171016
{
10181017
graphQLType = graphQLEnabled;
10191018
}

0 commit comments

Comments
 (0)