diff --git a/src/Core/Resolvers/BaseSqlQueryBuilder.cs b/src/Core/Resolvers/BaseSqlQueryBuilder.cs
index a509e9d842..ec4d799a57 100644
--- a/src/Core/Resolvers/BaseSqlQueryBuilder.cs
+++ b/src/Core/Resolvers/BaseSqlQueryBuilder.cs
@@ -193,6 +193,92 @@ protected virtual string Build(AggregationColumn column, bool useAlias = false)
return $"{column.Type.ToString()}({columnName}) {appendAlias}";
}
+ ///
+ /// Build the Group By Clause needed to append to the main query
+ ///
+ /// Sql query structure to build query on
+ /// SQL query with group-by clause
+ protected virtual string BuildGroupBy(SqlQueryStructure structure)
+ {
+ // Add GROUP BY clause if there are any group by columns
+ if (structure.GroupByMetadata.Fields.Any())
+ {
+ return $" GROUP BY {string.Join(", ", structure.GroupByMetadata.Fields.Values.Select(c => Build(c)))}";
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// Build the Having clause needed to append to the main query
+ ///
+ /// Sql query structure to build query on
+ /// SQL query with having clause
+ protected virtual string BuildHaving(SqlQueryStructure structure)
+ {
+ if (structure.GroupByMetadata.Aggregations.Count > 0)
+ {
+ List? havingPredicates = structure.GroupByMetadata.Aggregations
+ .SelectMany(aggregation => aggregation.HavingPredicates ?? new List())
+ .ToList();
+
+ if (havingPredicates.Any())
+ {
+ return $" HAVING {Build(havingPredicates)}";
+ }
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// Build the aggregation columns needed to append to the main query
+ ///
+ /// Sql query structure to build query on
+ /// SQL query with aggregation columns
+ protected virtual string BuildAggregationColumns(SqlQueryStructure structure)
+ {
+ string aggregations = string.Empty;
+ if (structure.GroupByMetadata.Aggregations.Count > 0)
+ {
+ if (structure.Columns.Any())
+ {
+ aggregations = $",{BuildAggregationColumns(structure.GroupByMetadata)}";
+ }
+ else
+ {
+ aggregations = $"{BuildAggregationColumns(structure.GroupByMetadata)}";
+ }
+ }
+
+ return aggregations;
+ }
+
+ ///
+ /// Build the aggregation columns needed to append to the main query
+ ///
+ /// GroupByMetadata
+ /// SQL query with aggregation columns
+ protected virtual string BuildAggregationColumns(GroupByMetadata metadata)
+ {
+ return string.Join(", ", metadata.Aggregations.Select(aggregation => Build(aggregation.Column, useAlias: true)));
+ }
+
+ ///
+ /// Build the Order By clause needed to append to the main query
+ ///
+ /// Sql query structure to build query on
+ /// SQL query with order-by clause
+ protected virtual string BuildOrderBy(SqlQueryStructure structure)
+ {
+ if (structure.OrderByColumns.Any())
+ {
+ return $" ORDER BY {Build(structure.OrderByColumns)}";
+ }
+
+ return string.Empty;
+ }
+
///
/// Build orderby column as
/// {SourceAlias}.{ColumnName} {direction}
@@ -447,7 +533,7 @@ public virtual string BuildForeignKeyInfoQuery(int numberOfParameters)
// constraint columns - one inner join for the columns from the 'Referencing table'
// and the other join for the columns from the 'Referenced Table'.
string foreignKeyQuery = $@"
-SELECT
+SELECT
ReferentialConstraints.CONSTRAINT_NAME {QuoteIdentifier(nameof(ForeignKeyDefinition))},
ReferencingColumnUsage.TABLE_SCHEMA
{QuoteIdentifier($"Referencing{nameof(DatabaseObject.SchemaName)}")},
@@ -457,9 +543,9 @@ public virtual string BuildForeignKeyInfoQuery(int numberOfParameters)
{QuoteIdentifier($"Referenced{nameof(DatabaseObject.SchemaName)}")},
ReferencedColumnUsage.TABLE_NAME {QuoteIdentifier($"Referenced{nameof(SourceDefinition)}")},
ReferencedColumnUsage.COLUMN_NAME {QuoteIdentifier(nameof(ForeignKeyDefinition.ReferencedColumns))}
-FROM
+FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ReferentialConstraints
- INNER JOIN
+ INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE ReferencingColumnUsage
ON ReferentialConstraints.CONSTRAINT_CATALOG = ReferencingColumnUsage.CONSTRAINT_CATALOG
AND ReferentialConstraints.CONSTRAINT_SCHEMA = ReferencingColumnUsage.CONSTRAINT_SCHEMA
diff --git a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs
index 1bf7e73ef4..7d0d2ef4b7 100644
--- a/src/Core/Resolvers/BaseTSqlQueryBuilder.cs
+++ b/src/Core/Resolvers/BaseTSqlQueryBuilder.cs
@@ -1,5 +1,4 @@
using Azure.DataApiBuilder.Config.ObjectModel;
-using Azure.DataApiBuilder.Core.Models;
namespace Azure.DataApiBuilder.Core.Resolvers
{
@@ -43,90 +42,5 @@ protected virtual string BuildPredicates(SqlQueryStructure structure)
Build(structure.PaginationMetadata.PaginationPredicate));
}
- ///
- /// Build the Group By Clause needed to append to the main query
- ///
- /// Sql query structure to build query on
- /// SQL query with group-by clause
- protected virtual string BuildGroupBy(SqlQueryStructure structure)
- {
- // Add GROUP BY clause if there are any group by columns
- if (structure.GroupByMetadata.Fields.Any())
- {
- return $" GROUP BY {string.Join(", ", structure.GroupByMetadata.Fields.Values.Select(c => Build(c)))}";
- }
-
- return string.Empty;
- }
-
- ///
- /// Build the Having clause needed to append to the main query
- ///
- /// Sql query structure to build query on
- /// SQL query with having clause
- protected virtual string BuildHaving(SqlQueryStructure structure)
- {
- if (structure.GroupByMetadata.Aggregations.Count > 0)
- {
- List? havingPredicates = structure.GroupByMetadata.Aggregations
- .SelectMany(aggregation => aggregation.HavingPredicates ?? new List())
- .ToList();
-
- if (havingPredicates.Any())
- {
- return $" HAVING {Build(havingPredicates)}";
- }
- }
-
- return string.Empty;
- }
-
- ///
- /// Build the Order By clause needed to append to the main query
- ///
- /// Sql query structure to build query on
- /// SQL query with order-by clause
- protected virtual string BuildOrderBy(SqlQueryStructure structure)
- {
- if (structure.OrderByColumns.Any())
- {
- return $" ORDER BY {Build(structure.OrderByColumns)}";
- }
-
- return string.Empty;
- }
-
- ///
- /// Build the aggregation columns needed to append to the main query
- ///
- /// Sql query structure to build query on
- /// SQL query with aggregation columns
- protected virtual string BuildAggregationColumns(SqlQueryStructure structure)
- {
- string aggregations = string.Empty;
- if (structure.GroupByMetadata.Aggregations.Count > 0)
- {
- if (structure.Columns.Any())
- {
- aggregations = $",{BuildAggregationColumns(structure.GroupByMetadata)}";
- }
- else
- {
- aggregations = $"{BuildAggregationColumns(structure.GroupByMetadata)}";
- }
- }
-
- return aggregations;
- }
-
- ///
- /// Build the aggregation columns needed to append to the main query
- ///
- /// GroupByMetadata
- /// SQL query with aggregation columns
- protected virtual string BuildAggregationColumns(GroupByMetadata metadata)
- {
- return string.Join(", ", metadata.Aggregations.Select(aggregation => Build(aggregation.Column, useAlias: true)));
- }
}
}
diff --git a/src/Core/Resolvers/PostgresQueryBuilder.cs b/src/Core/Resolvers/PostgresQueryBuilder.cs
index 00ac571149..9a768f82a0 100644
--- a/src/Core/Resolvers/PostgresQueryBuilder.cs
+++ b/src/Core/Resolvers/PostgresQueryBuilder.cs
@@ -41,10 +41,14 @@ public string Build(SqlQueryStructure structure)
Build(structure.Predicates),
Build(structure.PaginationMetadata.PaginationPredicate));
- string query = $"SELECT {MakeSelectColumns(structure)}"
+ string aggregations = BuildAggregationColumns(structure);
+
+ string query = $"SELECT {MakeSelectColumns(structure)}{aggregations}"
+ $" FROM {fromSql}"
+ $" WHERE {predicates}"
- + $" ORDER BY {Build(structure.OrderByColumns)}"
+ + BuildGroupBy(structure)
+ + BuildHaving(structure)
+ + BuildOrderBy(structure)
+ $" LIMIT {structure.Limit()}";
string subqueryName = QuoteIdentifier($"subq{structure.Counter.Next()}");
diff --git a/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs b/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs
index a2cc63b2c2..d744dc549e 100644
--- a/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs
+++ b/src/Service.GraphQLBuilder/Queries/QueryBuilder.cs
@@ -36,6 +36,7 @@ public static class QueryBuilder
{
DatabaseType.MSSQL,
DatabaseType.DWSQL,
+ DatabaseType.PostgreSQL,
};
///
diff --git a/src/Service.Tests/DatabaseSchema-PostgreSql.sql b/src/Service.Tests/DatabaseSchema-PostgreSql.sql
index bd36c551b0..77edd4a823 100644
--- a/src/Service.Tests/DatabaseSchema-PostgreSql.sql
+++ b/src/Service.Tests/DatabaseSchema-PostgreSql.sql
@@ -41,6 +41,7 @@ DROP TABLE IF EXISTS default_with_function_table;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS user_profiles;
DROP TABLE IF EXISTS dimaccount;
+DROP TABLE IF EXISTS date_only_table;
DROP FUNCTION IF EXISTS insertCompositeView;
DROP SCHEMA IF EXISTS foo;
@@ -361,14 +362,14 @@ INSERT INTO bookmarks (id, bkname)
SELECT
value,
CONCAT('Test Item #' , value)
-FROM
+FROM
GENERATE_SERIES(1, 10000, 1) as value;
INSERT INTO mappedbookmarks (id, bkname)
SELECT
value,
CONCAT('Test Item #' , value)
-FROM
+FROM
GENERATE_SERIES(1, 10000, 1) as value;
INSERT INTO GQLmappings(__column1, __column2, column3) VALUES (1, 'Incompatible GraphQL Name', 'Compatible GraphQL Name');
@@ -378,7 +379,7 @@ INSERT INTO GQLmappings(__column1, __column2, column3) VALUES (5, 'Filtered Reco
INSERT INTO publishers(id, name) VALUES (1234, 'Big Company'), (2345, 'Small Town Publisher'), (2323, 'TBD Publishing One'), (2324, 'TBD Publishing Two Ltd'), (1940, 'Policy Publisher 01'), (1941, 'Policy Publisher 02'), (1156, 'The First Publisher');
INSERT INTO clubs(id, name) VALUES (1111, 'Manchester United'), (1112, 'FC Barcelona'), (1113, 'Real Madrid');
INSERT INTO players(id, name, current_club_id, new_club_id)
- VALUES
+ VALUES
(1, 'Cristiano Ronaldo', 1113, 1111),
(2, 'Leonel Messi', 1112, 1113);
INSERT INTO authors(id, name, birthdate) VALUES (123, 'Jelte', '2001-01-01'), (124, 'Aniruddh', '2002-02-02'), (125, 'Aniruddh', '2001-01-01'), (126, 'Aaron', '2001-01-01');
@@ -487,3 +488,14 @@ $$ LANGUAGE plpgsql;
CREATE TRIGGER insertCompositeViewTrigger INSTEAD OF INSERT ON books_publishers_view_composite_insertable
FOR EACH ROW EXECUTE PROCEDURE insertCompositeView();
+
+CREATE TABLE date_only_table (
+ event_date date NOT NULL,
+ event_time time NOT NULL,
+ event_timestamp timestamp NOT NULL
+);
+
+INSERT INTO date_only_table(event_date, event_time, event_timestamp)
+VALUES ('2023-01-01', '08:30:00', '2023-01-01 08:30:00'),
+ ('2023-02-15', '12:45:00', '2023-02-15 12:45:00'),
+ ('2023-03-30', '17:15:00', '2023-03-30 17:15:00');
diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs
index 9136d2f5a6..abafea60db 100644
--- a/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs
+++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/PostgreSqlGraphQLQueryTests.cs
@@ -438,45 +438,49 @@ await TestConfigTakesPrecedenceForRelationshipFieldsOverDB(
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForAggregationsWithAliases()
{
- string msSqlQuery = @"
- SELECT
- MAX(categoryid) AS max,
- MAX(price) AS max_price,
- MIN(price) AS min_price,
- AVG(price) AS avg_price,
- SUM(price) AS sum_price
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ MAX(categoryid) AS max,
+ MAX(price) AS max_price,
+ MIN(price) AS min_price,
+ AVG(price) AS avg_price,
+ SUM(price) AS sum_price
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForAggregationsWithAliases(msSqlQuery);
+ await TestSupportForAggregationsWithAliases(postgresQuery);
}
///
/// Test to check GraphQL support for aggregations with aliases and groupby.
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
+ /// Note: an explicit ORDER BY categoryid is required here because DAB's actual generated query selects
+ /// categoryid as an extra column, which changes Postgres's query plan (and therefore GROUP BY row order)
+ /// compared to a query that only selects the aggregates.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForGroupByAggregationsWithAliases()
{
- string msSqlQuery = @"
- SELECT
- MAX(categoryid) AS max,
- MAX(price) AS max_price,
- MIN(price) AS min_price,
- AVG(price) AS avg_price,
- SUM(price) AS sum_price,
- COUNT(categoryid) AS count
- FROM stocks_price
- GROUP BY categoryid
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ MAX(categoryid) AS max,
+ MAX(price) AS max_price,
+ MIN(price) AS min_price,
+ AVG(price) AS avg_price,
+ SUM(price) AS sum_price,
+ COUNT(categoryid) AS count
+ FROM stocks_price
+ GROUP BY categoryid
+ ORDER BY categoryid
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForGroupByAggregationsWithAliases(msSqlQuery);
+ await TestSupportForGroupByAggregationsWithAliases(postgresQuery);
}
///
@@ -484,17 +488,17 @@ GROUP BY categoryid
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForMinAggregation()
{
- string msSqlQuery = @"
- SELECT
- MIN(price) AS min_price
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ MIN(price) AS min_price
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForMinAggregation(msSqlQuery);
+ await TestSupportForMinAggregation(postgresQuery);
}
///
@@ -502,17 +506,17 @@ FROM stocks_price
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForMaxAggregation()
{
- string msSqlQuery = @"
- SELECT
- MAX(price) AS max_price
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ MAX(price) AS max_price
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForMaxAggregation(msSqlQuery);
+ await TestSupportForMaxAggregation(postgresQuery);
}
///
@@ -520,17 +524,17 @@ FROM stocks_price
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForAvgAggregation()
{
- string msSqlQuery = @"
- SELECT
- AVG(price) AS avg_price
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ AVG(price) AS avg_price
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForAvgAggregation(msSqlQuery);
+ await TestSupportForAvgAggregation(postgresQuery);
}
///
@@ -538,17 +542,17 @@ FROM stocks_price
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForSumAggregation()
{
- string msSqlQuery = @"
- SELECT
- SUM(price) AS sum_price
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ SUM(price) AS sum_price
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForSumAggregation(msSqlQuery);
+ await TestSupportForSumAggregation(postgresQuery);
}
///
@@ -556,17 +560,17 @@ FROM stocks_price
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForCountAggregation()
{
- string msSqlQuery = @"
- SELECT
- COUNT(categoryid) AS count_categoryid
- FROM stocks_price
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ COUNT(categoryid) AS count_categoryid
+ FROM stocks_price
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForCountAggregation(msSqlQuery);
+ await TestSupportForCountAggregation(postgresQuery);
}
///
@@ -574,18 +578,19 @@ FROM stocks_price
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForHavingAggregation()
{
- string msSqlQuery = @"
- SELECT
- SUM(price) AS sum_price
- FROM stocks_price
- HAVING SUM(price) > 50
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ // HAVING may exclude the only row; json_agg over zero rows returns NULL, not [].
+ string postgresQuery = @"
+ SELECT COALESCE(json_agg(to_jsonb(table0)), '[]') FROM (
+ SELECT
+ MAX(id) AS max
+ FROM publishers
+ HAVING MAX(id) > 2346
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForHavingAggregation(msSqlQuery);
+ await TestSupportForHavingAggregation(postgresQuery);
}
///
@@ -593,19 +598,19 @@ HAVING SUM(price) > 50
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForGroupByHavingAggregation()
{
- string msSqlQuery = @"
- SELECT
- SUM(price) AS sum_price
- FROM stocks_price
- GROUP BY categoryid, pieceid
- HAVING SUM(price) > 50
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ SUM(price) AS sum_price
+ FROM stocks_price
+ GROUP BY categoryid, pieceid
+ HAVING SUM(price) > 50
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForGroupByHavingAggregation(msSqlQuery);
+ await TestSupportForGroupByHavingAggregation(postgresQuery);
}
///
@@ -613,22 +618,22 @@ HAVING SUM(price) > 50
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForGroupByHavingFieldsAggregation()
{
- string msSqlQuery = @"
- SELECT
- categoryid,
- pieceid,
- SUM(price) AS sum_price,
- COUNT(pieceid) AS count_piece
- FROM stocks_price
- GROUP BY categoryid, pieceid
- HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ categoryid,
+ pieceid,
+ SUM(price) AS sum_price,
+ COUNT(pieceid) AS count_piece
+ FROM stocks_price
+ GROUP BY categoryid, pieceid
+ HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForGroupByHavingFieldsAggregation(msSqlQuery);
+ await TestSupportForGroupByHavingFieldsAggregation(postgresQuery);
}
///
@@ -636,23 +641,22 @@ HAVING SUM(price) > 50 AND COUNT(pieceid) <= 100
/// This test verifies that the SQL query results are correctly mapped to the expected GraphQL format.
///
[TestMethod]
- [Ignore]
public async Task TestSupportForGroupByNoAggregation()
{
- string msSqlQuery = @"
- SELECT
- categoryid,
- pieceid
- FROM stocks_price
- GROUP BY categoryid, pieceid
- FOR JSON PATH, INCLUDE_NULL_VALUES";
+ string postgresQuery = @"
+ SELECT json_agg(to_jsonb(table0)) FROM (
+ SELECT
+ categoryid,
+ pieceid
+ FROM stocks_price
+ GROUP BY categoryid, pieceid
+ ) AS table0";
// Execute the test for the SQL query
- await TestSupportForGroupByNoAggregation(msSqlQuery);
+ await TestSupportForGroupByNoAggregation(postgresQuery);
}
[TestMethod]
- [Ignore]
public override async Task TestNoAggregationOptionsForTableWithoutNumericFields()
{
await base.TestNoAggregationOptionsForTableWithoutNumericFields();
diff --git a/src/Service.Tests/dab-config.PostgreSql.json b/src/Service.Tests/dab-config.PostgreSql.json
index 86bed1a352..fd4a34084a 100644
--- a/src/Service.Tests/dab-config.PostgreSql.json
+++ b/src/Service.Tests/dab-config.PostgreSql.json
@@ -2337,6 +2337,35 @@
}
]
},
+ "DateOnlyTable": {
+ "source": {
+ "object": "date_only_table",
+ "type": "table",
+ "key-fields": [
+ "event_date"
+ ]
+ },
+ "graphql": {
+ "enabled": true,
+ "type": {
+ "singular": "DateOnlyTable",
+ "plural": "DateOnlyTables"
+ }
+ },
+ "rest": {
+ "enabled": true
+ },
+ "permissions": [
+ {
+ "role": "anonymous",
+ "actions": [
+ {
+ "action": "*"
+ }
+ ]
+ }
+ ]
+ },
"GQLmappings": {
"source": {
"object": "gqlmappings",
@@ -2891,4 +2920,4 @@
}
}
}
-}
\ No newline at end of file
+}