Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
411f5c4
adding support for graphQL Queries
abhishekkumams Nov 14, 2022
c374f1b
resolving merge conflicts
abhishekkumams Nov 14, 2022
7b43e47
fixing doc
abhishekkumams Nov 14, 2022
5774c0f
fix formatting
abhishekkumams Nov 14, 2022
58cca3e
adding support for mutation
abhishekkumams Nov 15, 2022
f7015ce
fixing mutation queries
abhishekkumams Nov 16, 2022
c6cfd93
fixing temp changes
abhishekkumams Nov 16, 2022
3bb2670
cleaning code
abhishekkumams Nov 16, 2022
270a599
fix formatting
abhishekkumams Nov 16, 2022
15781bd
removing redundant
abhishekkumams Nov 16, 2022
eb34b0d
adding tests
abhishekkumams Nov 17, 2022
74decf4
fix argument type for graphQL
abhishekkumams Nov 18, 2022
615bb74
fixing field level authorization
abhishekkumams Nov 20, 2022
61eff13
fixing build
abhishekkumams Nov 20, 2022
a36f246
added new tests
abhishekkumams Nov 21, 2022
ee08d62
fix formatting
abhishekkumams Nov 21, 2022
c151678
fix commands file
abhishekkumams Nov 21, 2022
bd6eab2
resolve conflicts
abhishekkumams Nov 21, 2022
f841fdc
fixing tests
abhishekkumams Nov 21, 2022
d5016da
fixing tests
abhishekkumams Nov 21, 2022
1b45a78
using GetDefinedSingularName for stored-procedure graphQL
abhishekkumams Nov 21, 2022
bf62575
fix formatting
abhishekkumams Nov 21, 2022
2017c74
fixing tests
abhishekkumams Nov 21, 2022
07e47bc
fixing tests
abhishekkumams Nov 21, 2022
ae84c77
fixing pagination tests
abhishekkumams Nov 21, 2022
42d6058
Merge branch 'main' into dev/abhishekkuma/add-graphql-support-for-sto…
abhishekkumams Nov 21, 2022
1171786
fixing test
abhishekkumams Nov 22, 2022
43c07c2
Merge branch 'dev/abhishekkuma/add-graphql-support-for-stored-procedu…
abhishekkumams Nov 22, 2022
293983b
fix formatting
abhishekkumams Nov 22, 2022
9c7f13d
updating MsSQL commands file
abhishekkumams Nov 22, 2022
a9c2536
fixing rest tests
abhishekkumams Nov 23, 2022
091bfbf
fixing tests
abhishekkumams Nov 23, 2022
a8fc53a
adding new tests
abhishekkumams Nov 23, 2022
3c35ae7
fix formatting
abhishekkumams Nov 23, 2022
d6af949
fixing test
abhishekkumams Nov 23, 2022
56257fe
Merge branch 'main' into dev/abhishekkuma/add-graphql-support-for-sto…
abhishekkumams Nov 23, 2022
58ade56
fix formatting
abhishekkumams Nov 23, 2022
8f7b19a
Merge branch 'dev/abhishekkuma/add-graphql-support-for-stored-procedu…
abhishekkumams Nov 23, 2022
53a0ebb
reomoving comented code
abhishekkumams Nov 25, 2022
597c3ca
fix formatting
abhishekkumams Nov 25, 2022
eba1b6a
Merge branch 'main' into dev/abhishekkuma/add-graphql-support-for-sto…
abhishekkumams Nov 25, 2022
18d0a09
Merge branch 'main' into dev/abhishekkuma/add-graphql-support-for-sto…
abhishekkumams Nov 25, 2022
3e3c744
fixed action validation for stored procedure
abhishekkumams Nov 29, 2022
e37dfe0
Merge branch 'dev/abhishekkuma/add-graphql-support-for-stored-procedu…
abhishekkumams Nov 29, 2022
c364c36
updating docs
abhishekkumams Nov 29, 2022
44dabe8
updating stored-procedures
abhishekkumams Nov 29, 2022
62c80c0
fixing tests
abhishekkumams Nov 29, 2022
6dbf262
resolving merge conflicts
abhishekkumams Nov 29, 2022
e3b3e10
Merge branch 'main' into dev/abhishekkuma/add-graphql-support-for-sto…
abhishekkumams Nov 29, 2022
fbb329b
fix formatting
abhishekkumams Nov 29, 2022
73d3dc6
Merge branch 'dev/abhishekkuma/add-graphql-support-for-stored-procedu…
abhishekkumams Nov 29, 2022
eca1846
resolving comments
abhishekkumams Nov 29, 2022
0a5e200
moved GetSourcedefinition to DatabaseObject class
abhishekkumams Nov 29, 2022
961885f
resolved nits
abhishekkumams Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions ConfigGenerators/MsSqlCommands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ add Journal --config "dab-config.MsSql.json" --source "journals" --rest true --g
add ArtOfWar --config "dab-config.MsSql.json" --source "aow" --rest true --permissions "anonymous:*"
add series --config "dab-config.MsSql.json" --source "series" --permissions "anonymous:*"
add Sales --config "dab-config.MsSql.json" --source "sales" --permissions "anonymous:*" --rest true --graphql true
add GetBooks --config "dab-config.MsSql.json" --source "get_books" --source.type "stored-procedure" --permissions "anonymous:read" --rest true --graphql false
add GetBooks --config "dab-config.MsSql.json" --source "get_books" --source.type "stored-procedure" --permissions "anonymous:read" --rest true --graphql true
add GetBook --config "dab-config.MsSql.json" --source "get_book_by_id" --source.type "stored-procedure" --permissions "anonymous:read" --rest true --graphql false
add GetPublisher --config "dab-config.MsSql.json" --source "get_publisher_by_id" --source.type "stored-procedure" --source.params "id:1" --permissions "anonymous:read" --rest true --graphql true
add InsertBook --config "dab-config.MsSql.json" --source "insert_book" --source.type "stored-procedure" --source.params "title:randomX,publisher_id:1234" --permissions "anonymous:create" --rest true --graphql true
add CountBooks --config "dab-config.MsSql.json" --source "count_books" --source.type "stored-procedure" --permissions "anonymous:read" --rest true --graphql true
add DeleteLastInsertedBook --config "dab-config.MsSql.json" --source "delete_last_inserted_book" --source.type "stored-procedure" --permissions "anonymous:delete" --rest true --graphql true
add UpdateBookTitle --config "dab-config.MsSql.json" --source "update_book_title" --source.type "stored-procedure" --source.params "id:1,title:Testing Tonight" --permissions "anonymous:update" --rest true --graphql true
update Publisher --config "dab-config.MsSql.json" --permissions "authenticated:create,read,update,delete" --rest true --graphql true --relationship books --target.entity Book --cardinality many
update Publisher --config "dab-config.MsSql.json" --permissions "policy_tester_01:create,delete"
update Publisher --config "dab-config.MsSql.json" --permissions "policy_tester_01:update" --fields.include "*"
Expand Down Expand Up @@ -106,6 +111,11 @@ update Journal --config "dab-config.MsSql.json" --permissions "policy_tester_upd
update Journal --config "dab-config.MsSql.json" --permissions "policy_tester_update_noread:delete" --fields.include "*" --policy-database "@item.id eq 1"
update Journal --config "dab-config.MsSql.json" --permissions "authorizationHandlerTester:read"
update ArtOfWar --config "dab-config.MsSql.json" --permissions "authenticated:*" --map "DetailAssessmentAndPlanning:始計,WagingWar:作戰,StrategicAttack:謀攻,NoteNum:┬─┬ノ( º _ ºノ)"
update GetBook --config "dab-config.MsSql.json" --permissions "authenticated:*"
update GetBooks --config "dab-config.MsSql.json" --permissions "authenticated:*"
update GetBook --config "dab-config.MsSql.json" --permissions "authenticated:read"
update GetPublisher --config "dab-config.MsSql.json" --permissions "authenticated:read"
update GetBooks --config "dab-config.MsSql.json" --permissions "authenticated:read"
update InsertBook --config "dab-config.MsSql.json" --permissions "authenticated:create"
update CountBooks --config "dab-config.MsSql.json" --permissions "authenticated:read"
update DeleteLastInsertedBook --config "dab-config.MsSql.json" --permissions "authenticated:delete"
update UpdateBookTitle --config "dab-config.MsSql.json" --permissions "authenticated:update"
update Sales --config "dab-config.MsSql.json" --permissions "authenticated:*"
131 changes: 128 additions & 3 deletions ConfigGenerators/dab-config.sql.reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -916,15 +916,15 @@
"object": "get_books"
},
"rest": true,
"graphql": false,
"graphql": true,
"permissions": [
{
"role": "anonymous",
"actions": [ "read" ]
},
{
"role": "authenticated",
"actions": [ "*" ]
"actions": [ "read" ]
}
]
},
Expand All @@ -942,7 +942,7 @@
},
{
"role": "authenticated",
"actions": [ "*" ]
"actions": [ "read" ]
}
]
},
Expand All @@ -968,6 +968,131 @@
]
}
]
},
"InsertBook": {
"source": {
"type": "stored-procedure",
"object": "insert_book",
"parameters": {
"title": "randomX",
"publisher_id": 1234
},
"key-fields": []
},
"rest": true,
"permissions": [
{
"role": "anonymous",
"actions": [
"create"
]
},
{
"role": "authenticated",
"actions": [
"create"
]
}
],
"graphql": true
},
"CountBooks": {
"source": {
"type": "stored-procedure",
"object": "count_books",
"key-fields": []
},
"rest": true,
"permissions": [
{
"role": "anonymous",
"actions": [
"read"
]
},
{
"role": "authenticated",
"actions": [
"read"
]
}
],
"graphql": true
},
"DeleteLastInsertedBook": {
"source": {
"type": "stored-procedure",
"object": "delete_last_inserted_book",
"key-fields": []
},
"rest": true,
"permissions": [
{
"role": "anonymous",
"actions": [
"delete"
]
},
{
"role": "authenticated",
"actions": [
"delete"
]
}
],
"graphql": true
},
"UpdateBookTitle": {
"source": {
"type": "stored-procedure",
"object": "update_book_title",
"parameters": {
"id": 1,
"title": "Testing Tonight"
},
"key-fields": []
},
"rest": true,
"permissions": [
{
"role": "anonymous",
"actions": [
"update"
]
},
{
"role": "authenticated",
"actions": [
"update"
]
}
],
"graphql": true
},
"GetPublisher": {
"source": {
"type": "stored-procedure",
"object": "get_publisher_by_id",
"parameters": {
"id": 1
}
},
"rest": true,
"permissions": [
{
"role": "anonymous",
"actions": [
"read"
]
},
{
"role": "authenticated",
"actions": [
"read"
]
}
],
"graphql": true
}
}
}
5 changes: 4 additions & 1 deletion docs/views-and-stored-procedures.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ the `dab-config.json` file will look like the following:

The `parameters` object is optional, and is used to provide default values to be passed to the stored procedure parameters, if those are not provided in the HTTP request.

**ATTENTION**: Only the first result set returned by the stored procedure will be used by Data API Builder.
**ATTENTION**:
1. Only the first result set returned by the stored procedure will be used by Data API Builder.
2. Currently we only support simple StoredProcedures,i.e. stored procedure that requires only 1 CRUD action to execute.
3. If more than one CRUD action is specified in the config, runtime initialization will fail due to config validation error.

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.

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/src/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ private static bool TryGetUpdatedSourceObjectWithOptions(
}

// If given SourceParameter is null or is Empty, no update is required.
// Else updatedSourceParameters will contain the parsed dictionary of parameters.
// Else updatedSourceParameters will contain the parsed dictionary of parameters.
if (options.SourceParameters is not null && options.SourceParameters.Any() &&
!TryParseSourceParameterDictionary(options.SourceParameters, out updatedSourceParameters))
{
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/src/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ private static object ParseStringValue(string stringValue)
{
return floatingValue;
}
else if (Boolean.TryParse(stringValue, out bool booleanValue))
else if (bool.TryParse(stringValue, out bool booleanValue))
{
return booleanValue;
}
Expand Down
27 changes: 26 additions & 1 deletion src/Config/DatabaseObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ public override int GetHashCode()
{
return HashCode.Combine(SchemaName, Name);
}

/// <summary>
/// Get the underlying SourceDefinition based on database object source type
/// </summary>
public SourceDefinition SourceDefinition
{
get
{
return SourceType switch
{
SourceType.Table => ((DatabaseTable)this).TableDefinition,
SourceType.View => ((DatabaseView)this).ViewDefinition,
SourceType.StoredProcedure => ((DatabaseStoredProcedure)this).StoredProcedureDefinition,
_ => throw new Exception(
message: $"Unsupported SourceType. It can either be Table,View, or Stored Procedure.")
};
}
}
}

/// <summary>
Expand Down Expand Up @@ -77,7 +95,7 @@ public DatabaseStoredProcedure(string schemaName, string tableName)
public StoredProcedureDefinition StoredProcedureDefinition { get; set; } = null!;
}

public class StoredProcedureDefinition
public class StoredProcedureDefinition : SourceDefinition
{
/// <summary>
/// The list of input parameters
Expand Down Expand Up @@ -161,6 +179,13 @@ public class ColumnDefinition
public bool IsAutoGenerated { get; set; }
public bool IsNullable { get; set; }
public object? DefaultValue { get; set; }

public ColumnDefinition() { }

public ColumnDefinition(Type systemType)
{
this.SystemType = systemType;
}
}

public class ForeignKeyDefinition
Expand Down
6 changes: 3 additions & 3 deletions src/Config/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,21 @@ public void TryPopulateSourceFields()
throw new JsonException(message: "Must specify entity source.");
}

JsonElement sourceJson = (JsonElement)Source;
JsonElement sourceJson = JsonSerializer.SerializeToElement(Source);

// In the case of a simple, string source, we assume the source type is a table; parameters and key fields left null
// Note: engine supports views backing entities labeled as Tables, as long as their primary key can be inferred
if (sourceJson.ValueKind is JsonValueKind.String)
{
ObjectType = SourceType.Table;
SourceName = JsonSerializer.Deserialize<string>((JsonElement)Source)!;
SourceName = JsonSerializer.Deserialize<string>(sourceJson)!;
Parameters = null;
KeyFields = null;
}
else if (sourceJson.ValueKind is JsonValueKind.Object)
{
DatabaseObjectSource? objectSource
= JsonSerializer.Deserialize<DatabaseObjectSource>((JsonElement)Source,
= JsonSerializer.Deserialize<DatabaseObjectSource>(sourceJson,
options: RuntimeConfig.SerializerOptions);

if (objectSource is null)
Expand Down
97 changes: 97 additions & 0 deletions src/Service.GraphQLBuilder/GraphQLStoredProcedureBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Text.Json;
using Azure.DataApiBuilder.Config;
using HotChocolate.Language;
using HotChocolate.Types;
using static Azure.DataApiBuilder.Service.GraphQLBuilder.GraphQLUtils;

namespace Azure.DataApiBuilder.Service.GraphQLBuilder
{
public static class GraphQLStoredProcedureBuilder
{
/// <summary>
/// Helper function to create StoredProcedure Schema for GraphQL.
/// It uses the parameters to build the arguments and returns a list
/// of the StoredProcedure GraphQL object.
/// </summary>
public static FieldDefinitionNode GenerateStoredProcedureSchema(
NameNode name,
Entity entity,
IEnumerable<string>? rolesAllowed = null)
{
List<InputValueDefinitionNode> inputValues = new();
List<DirectiveNode> fieldDefinitionNodeDirectives = new();

if (entity.Parameters is not null)
{
foreach (string param in entity.Parameters.Keys)
{
Tuple<string, IValueNode> defaultGraphQLValue = GetGraphQLTypeAndNodeTypeFromStringValue(entity.Parameters[param].ToString()!);
inputValues.Add(
new(
location: null,
new(param),
new StringValueNode($"parameters for {name.Value} stored-procedure"),
new NamedTypeNode(defaultGraphQLValue.Item1),
defaultValue: defaultGraphQLValue.Item2,
new List<DirectiveNode>())
);
}
}

if (CreateAuthorizationDirectiveIfNecessary(
rolesAllowed,
out DirectiveNode? authorizeDirective))
{
fieldDefinitionNodeDirectives.Add(authorizeDirective!);
}

return new(
location: null,
new NameNode(name.Value),
new StringValueNode($"Execute Stored-Procedure {name.Value} and get results from the database"),
inputValues,
new NonNullTypeNode(new ListTypeNode(new NonNullTypeNode(new NamedTypeNode(name)))),
fieldDefinitionNodeDirectives
);
}

/// <summary>
/// Takes the result from DB as JsonDocument and formats it in a way that can be filtered by column
/// name. It parses the Json document into a list of Dictionary with key as result_column_name
/// with it's corresponding value.
/// returns an empty list in case of no result
/// or stored-procedure is trying to read from DB without READ permission.
/// </summary>
public static List<JsonDocument> FormatStoredProcedureResultAsJsonList(bool IsReadAllowed, JsonDocument jsonDocument)
{
if (jsonDocument is null || !IsReadAllowed)
{
return new List<JsonDocument>();
}

List<JsonDocument> resultJson = new();
List<Dictionary<string, object>> resultList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(jsonDocument.RootElement.ToString())!;
foreach (Dictionary<string, object> result in resultList)
{
resultJson.Add(JsonDocument.Parse(JsonSerializer.Serialize(result)));
}

return resultJson;
}

/// <summary>
/// Helper method to create a default result field for stored-procedure which does not
/// return any row.
/// </summary>
public static FieldDefinitionNode GetDefaultResultFieldForStoredProcedure()
{
return new(
location: null,
new("result"),
description: new StringValueNode("Contains output of stored-procedure execution"),
new List<InputValueDefinitionNode>(),
new StringType().ToTypeNode(),
new List<DirectiveNode>());
}
}
}
Loading