Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
f54923c
Fix naming casing issue by removing step to format entity names for G…
seantleonard Jul 27, 2022
ede5824
add comments.
seantleonard Jul 28, 2022
663a0f4
Add helpers to check GraphQL naming violations.
seantleonard Jul 28, 2022
a456f79
validation of entity names for GraphQL.
seantleonard Jul 28, 2022
819245e
Remove commented code.
seantleonard Jul 28, 2022
e5a1c4d
explicitly singularizing entity names for delete mutation for schema …
severussundar Jul 29, 2022
35b4ba3
fix formatting
severussundar Jul 29, 2022
653fa99
adding test summary
severussundar Jul 29, 2022
8aa73ab
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 2, 2022
3b150b0
using an older version to see if the right display name comes up in p…
severussundar Aug 2, 2022
718b6fc
switching version to 2.2.6
severussundar Aug 2, 2022
d8ff5ad
removing singularizing logic
severussundar Aug 3, 2022
f775d29
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 3, 2022
ef57789
merge main
seantleonard Aug 4, 2022
788aa7d
changes for creating queries and mutations, tests as per naming conve…
severussundar Aug 4, 2022
01fde38
fixing formatting
severussundar Aug 4, 2022
cf33b24
removing an extra blank line
severussundar Aug 4, 2022
ac74e4b
minor changes in comments
severussundar Aug 4, 2022
1c274d9
Added comments to GraphQLNaming functions
seantleonard Aug 4, 2022
db23c37
incorporating review comments
severussundar Aug 5, 2022
48b481e
passing the name as a string for building queries
severussundar Aug 5, 2022
00e479c
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 5, 2022
76f3433
fix formatting
severussundar Aug 5, 2022
84036de
fixing formatting
severussundar Aug 5, 2022
35211be
Remove extra name formatting and update comments.
seantleonard Aug 5, 2022
d91a56c
Add format naming back without santizing
seantleonard Aug 5, 2022
381e1be
Updated comments and logic.
seantleonard Aug 5, 2022
c21360d
Merge branch 'main' into dev/seleonar/gqlNamingFix
seantleonard Aug 5, 2022
ef76da9
Adding tests and comments.
seantleonard Aug 5, 2022
a98beb5
remove space
seantleonard Aug 5, 2022
7b8e32b
renaming varaiables, methods for better readability
severussundar Aug 8, 2022
43c2f9c
Fix entity names to be case-sensitive to match test cases: Magazine -…
seantleonard Aug 9, 2022
5e61705
GraphQL Naming - deserialize json properly and set graphql schema nam…
seantleonard Aug 9, 2022
2f61db5
Merge branch 'main' into dev/seleonar/gqlNamingFix
seantleonard Aug 9, 2022
55482f3
Remove commented code and whitespace.
seantleonard Aug 9, 2022
68c03a0
Merge branch 'dev/seleonar/gqlNamingFix' of https://github.com/Azure/…
seantleonard Aug 9, 2022
a1f5933
Match test pattern, no catch block, as tests that throw exception wil…
seantleonard Aug 9, 2022
8772e6f
merge main
seantleonard Aug 9, 2022
767e451
fix tests and merging of DAB name changes.
seantleonard Aug 9, 2022
ed92c86
Get Model name for graphQL entity in mutation engine for use in autho…
seantleonard Aug 9, 2022
41d9484
Update config files to set GraphQL singular plural, so that tests don…
seantleonard Aug 9, 2022
18cd739
Update tests to use the proper casing of graphQL queries/field names …
seantleonard Aug 9, 2022
9a313ea
Fix mutation tests.
seantleonard Aug 9, 2022
cbafe33
Fix spacing.
seantleonard Aug 9, 2022
ef104cc
Merge branch 'main' into dev/seleonar/gqlNamingFix
seantleonard Aug 9, 2022
31281ea
Fix spacing and usings.
seantleonard Aug 9, 2022
628d385
fix using ordering.
seantleonard Aug 9, 2022
d0f0d3d
Fixing more tests. and updating configs to reflect graphqlnaming
seantleonard Aug 10, 2022
9a4228f
fixing merge conflicts
severussundar Aug 10, 2022
c6f3e62
fixing merge with main
severussundar Aug 10, 2022
01f0bd7
making collection query start with lowercase alphabet
severussundar Aug 10, 2022
5690dfe
fix rest tests due to changed entity name "magazine" no longer having…
seantleonard Aug 10, 2022
922b3a8
Fix the config files to properly set GraphQL singular/plural.
seantleonard Aug 10, 2022
c54ae69
adding singularPlural type to website, using lower-case for magazine …
severussundar Aug 10, 2022
b4d0a0f
Update model name for Cosmos GQL Schema.
seantleonard Aug 10, 2022
3420c6a
changing the case of the query
severussundar Aug 10, 2022
72bfb5b
removing formatting of entity names in queries
severussundar Aug 10, 2022
3bbb49a
fixing unit tests
severussundar Aug 10, 2022
83948a4
fixing tests to use correct casing in the queries
severussundar Aug 10, 2022
9f7446a
attempt at fixing deserialization issue
severussundar Aug 10, 2022
2c624a5
fix formatting
severussundar Aug 10, 2022
5dc5464
checking if response json is valid
severussundar Aug 10, 2022
73e2467
using serialization instead of to string
severussundar Aug 10, 2022
70d9205
adding graphql types for mysql and pgsql, removing newtonsoft seriali…
severussundar Aug 10, 2022
6517157
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 10, 2022
b7bac20
using same case for queries as entity name for cosmos tests
severussundar Aug 10, 2022
7d70f53
chaning query names in cosmos tests
severussundar Aug 10, 2022
88eba79
update MySql/PG config.
seantleonard Aug 10, 2022
2b9b956
Revert "fixing tests to use correct casing in the queries"
severussundar Aug 10, 2022
e24dd50
removing httpclient headers
severussundar Aug 10, 2022
3dd4cb8
Fix Cosmos Tests and Logic for building Mutations.
seantleonard Aug 11, 2022
dfce151
Fix broken tests affected by GraphQL Naming.
seantleonard Aug 11, 2022
320afc5
REmove unnecessary code.
seantleonard Aug 11, 2022
84dfaa1
Remove old / extra code for formatting that is not used due to honori…
seantleonard Aug 11, 2022
0c40d70
Merge branch 'main' into dev/seleonar/gqlNamingFix
seantleonard Aug 11, 2022
a9f4848
remove extra spacing not caught by local dotnet format. or was lost i…
seantleonard Aug 11, 2022
08ec9cb
Merge branch 'dev/seleonar/gqlNamingFix' of https://github.com/Azure/…
seantleonard Aug 11, 2022
59dd9c9
fix comments
seantleonard Aug 11, 2022
55b6a9f
Updated comment.
seantleonard Aug 11, 2022
2ece755
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 11, 2022
a7a8c27
merging latest changes from GraphQL Entity Naming PR
severussundar Aug 11, 2022
0a56126
throwing exception when singular name is not defined for GraphQL obje…
severussundar Aug 11, 2022
ec04c9d
fix formatting
severussundar Aug 11, 2022
f2e358a
fixing formatting
severussundar Aug 11, 2022
035dcf5
removing redundant null check
severussundar Aug 11, 2022
d051f52
adding display names to unit tests
severussundar Aug 11, 2022
e4ef6e7
Updated comments/grammar, usage of regex to not allow spaces and fix …
seantleonard Aug 11, 2022
2c310ff
Update model directive to be properly formed.
seantleonard Aug 11, 2022
0dd07e0
adds graphql naming validations to singular, plural names
severussundar Aug 11, 2022
f37d4d7
Merge remote-tracking branch 'origin/dev/seleonar/gqlNamingFix' into …
severussundar Aug 11, 2022
5a65a88
fixing formatting
severussundar Aug 11, 2022
0fc078a
merge main
seantleonard Aug 12, 2022
6678a58
keeping formatting for query fields, fixing tests
severussundar Aug 12, 2022
a439811
fixing formatting
severussundar Aug 12, 2022
26e3596
removing graphql validation function, addresing review comments
severussundar Aug 12, 2022
d9d21ac
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 12, 2022
fb6c872
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 12, 2022
1a17e83
fixing model directive
severussundar Aug 12, 2022
5c15d8b
Merge branch 'main' into dev/shyamsundarj/singularizing-entity-names-…
severussundar Aug 12, 2022
80b6891
addressing revew suggestions
severussundar Aug 12, 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
7 changes: 1 addition & 6 deletions src/Service.GraphQLBuilder/GraphQLNaming.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ public static string FormatNameForField(string name)
return string.Join("", nameSegments.Select((n, i) => $"{(i == 0 ? char.ToLowerInvariant(n[0]) : char.ToUpperInvariant(n[0]))}{n[1..]}"));
}

public static string FormatNameForField(NameNode name)
{
return FormatNameForField(name.Value);
}

/// <summary>
/// Helper to pluralize the value of a NameNode HotChocolate schema object
/// </summary>
Expand Down Expand Up @@ -178,7 +173,7 @@ public static NameNode Pluralize(string name, Entity configEntity)
return new NameNode(namingRules.Plural);
}

return new NameNode(name.Pluralize());
return new NameNode(name.Pluralize(inputIsKnownToBeSingular: false));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,11 @@ public static FieldDefinitionNode Build(
fieldDefinitionNodeDirectives.Add(authorizeDirective!);
}

string singularName = GetDefinedSingularName(name.Value, entity);
return new(
location: null,
new NameNode($"create{GetDefinedSingularName(name.Value, entity)}"),
new StringValueNode($"Creates a new {name}"),
new NameNode($"create{singularName}"),
new StringValueNode($"Creates a new {singularName}"),
new List<InputValueDefinitionNode> {
new InputValueDefinitionNode(
location : null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ public static FieldDefinitionNode Build(
fieldDefinitionNodeDirectives.Add(authorizeDirective!);
}

string singularName = GetDefinedSingularName(name.Value, configEntity);
return new(
null,
new NameNode($"delete{GetDefinedSingularName(name.Value, configEntity)}"),
new StringValueNode($"Delete a {name}"),
new NameNode($"delete{singularName}"),
new StringValueNode($"Delete a {singularName}"),
inputValues,
new NamedTypeNode(name),
fieldDefinitionNodeDirectives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,11 @@ public static FieldDefinitionNode Build(
fieldDefinitionNodeDirectives.Add(authorizeDirective!);
}

string singularName = GetDefinedSingularName(name.Value, entities[dbEntityName]);
return new(
location: null,
new NameNode($"update{GetDefinedSingularName(name.Value, entities[dbEntityName])}"),
new StringValueNode($"Updates a {name}"),
new NameNode($"update{singularName}"),
new StringValueNode($"Updates a {singularName}"),
inputValues,
new NamedTypeNode(name),
fieldDefinitionNodeDirectives
Expand Down
18 changes: 12 additions & 6 deletions src/Service.GraphQLBuilder/Queries/QueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static DocumentNode Build(
if (rolesAllowedForRead.Count() > 0)
{
queryFields.Add(GenerateGetAllQuery(objectTypeDefinitionNode, name, returnType, inputTypes, entity, rolesAllowedForRead));
queryFields.Add(GenerateByPKQuery(objectTypeDefinitionNode, name, databaseType, rolesAllowedForRead));
queryFields.Add(GenerateByPKQuery(objectTypeDefinitionNode, name, databaseType, entity, rolesAllowedForRead));
}
}
}
Expand All @@ -70,7 +70,12 @@ public static DocumentNode Build(
return new(definitionNodes);
}

public static FieldDefinitionNode GenerateByPKQuery(ObjectTypeDefinitionNode objectTypeDefinitionNode, NameNode name, DatabaseType databaseType, IEnumerable<string>? rolesAllowedForRead = null)
public static FieldDefinitionNode GenerateByPKQuery(
ObjectTypeDefinitionNode objectTypeDefinitionNode,
NameNode name,
DatabaseType databaseType,
Entity entity,
IEnumerable<string>? rolesAllowedForRead = null)
{
IEnumerable<FieldDefinitionNode> primaryKeyFields =
FindPrimaryKeyFields(objectTypeDefinitionNode, databaseType);
Expand All @@ -95,10 +100,11 @@ public static FieldDefinitionNode GenerateByPKQuery(ObjectTypeDefinitionNode obj
new List<DirectiveNode>()));
}

string singularName = GetDefinedSingularName(name.Value, entity);
return new(
location: null,
new NameNode($"{FormatNameForField(name)}_by_pk"),
new StringValueNode($"Get a {name} from the database by its ID/primary key"),
new NameNode($"{FormatNameForField(singularName)}_by_pk"),
new StringValueNode($"Get a {singularName} from the database by its ID/primary key"),
inputValues,
new NamedTypeNode(name),
fieldDefinitionNodeDirectives
Expand Down Expand Up @@ -141,8 +147,8 @@ public static FieldDefinitionNode GenerateGetAllQuery(
// books(first: Int, after: String, filter: BooksFilterInput, orderBy: BooksOrderByInput): BooksConnection!
return new(
location: null,
new NameNode(FormatNameForField(Pluralize(name, entity))),
new StringValueNode($"Get a list of all the {name} items from the database"),
new NameNode(FormatNameForField(Pluralize(name, entity).Value)),
Comment thread
severussundar marked this conversation as resolved.
new StringValueNode($"Get a list of all the {GetDefinedSingularName(name.Value, entity)} items from the database"),
QueryArgumentsForField(filterInputName, orderByInputName),
new NonNullTypeNode(new NamedTypeNode(returnType.Name)),
fieldDefinitionNodeDirectives
Expand Down
58 changes: 58 additions & 0 deletions src/Service.Tests/GraphQLBuilder/Helpers/GraphQLTestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ namespace Azure.DataApiBuilder.Service.Tests.GraphQLBuilder.Helpers
{
public static class GraphQLTestHelpers
{
public const string BOOK_GQL =
@"
type Book @model(name:""Book"") {
book_id: Int! @primaryKey
}
";

public const string BOOKS_GQL =
@"
type Books @model(name:""Books"") {
book_id: Int! @primaryKey
}
";

public const string PERSON_GQL =
@"
type Person @model(name:""Person"") {
person_id: Int! @primaryKey
}
";

public const string PEOPLE_GQL =
@"
type People @model(name:""People"") {
people_id: Int! @primaryKey
}
";

/// <summary>
/// Mock the entityPermissionsMap which resolves which roles need to be included
/// in an authorize directive used on a GraphQL object type definition.
Expand Down Expand Up @@ -46,6 +74,36 @@ public static Entity GenerateEmptyEntity()
return new Entity("foo", Rest: null, GraphQL: null, Array.Empty<PermissionSetting>(), Relationships: new(), Mappings: new());
}

/// <summary>
/// Creates an entity with a SingularPlural GraphQL type.
/// </summary>
/// <param name="singularNameForEntity"> Singular name defined by user in the config.</param>
/// <param name="pluralNameForEntity"> Plural name defined by user in the config.</param>
public static Entity GenerateEntityWithSingularPlural(string singularNameForEntity, string pluralNameForEntity)
{
return new Entity(Source: "foo",
Rest: null,
GraphQL: new GraphQLEntitySettings(new SingularPlural(singularNameForEntity, pluralNameForEntity)),
Permissions: Array.Empty<PermissionSetting>(),
Relationships: new(),
Mappings: new());
}

/// <summary>
/// Creates an entity with a string GraphQL type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static Entity GenerateEntityWithStringType(string type)
{
return new Entity(Source: "foo",
Rest: null,
GraphQL: new GraphQLEntitySettings(type),
Permissions: Array.Empty<PermissionSetting>(),
Relationships: new(),
Mappings: new());
}

/// <summary>
/// Ensures that for each fieldDefinition present:
/// - One @authorize directive found
Expand Down
79 changes: 79 additions & 0 deletions src/Service.Tests/GraphQLBuilder/MutationBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -923,5 +923,84 @@ public static ObjectTypeDefinitionNode GetMutationNode(DocumentNode mutationRoot
FieldDefinitionNode field = query.Fields.First(f => f.Name.Value == $"updateFoo");
return (mutationRoot, field);
}

/// <summary>
/// We assume that the user will provide a singular name for the entity. Users have the option of providing singular and
/// plural names for an entity in the config to have more control over the graphql schema generation.
/// When singular and plural names are specified by the user, these names will be used for generating the
/// queries and mutations in the schema.
/// When singular and plural names are not provided, the queries and mutations will be generated with the entity's name.
/// This test validates that this naming convention is followed for the mutations when the schema is generated.
/// </summary>
/// <param name="gql">Type definition for the entity</param>
/// <param name="entityName">Name of the entity</param>
/// <param name="singularName">Singular name provided by the user</param>
/// <param name="pluralName">Plural name provided by the user</param>
/// <param name="expectedName"> Expected name of the entity in the mutation. Used to construct the exact expected mutation names.</param>
[DataTestMethod]
[DataRow(GraphQLTestHelpers.PEOPLE_GQL, "People", null, null, "People",
DisplayName = "Mutation name and description validation for singular entity name with singular plural not defined")]
[DataRow(GraphQLTestHelpers.PEOPLE_GQL, "People", "Person", "People", "Person",
DisplayName = "Mutaiton name and description validation for plural entity name with singular plural defined")]
[DataRow(GraphQLTestHelpers.PEOPLE_GQL, "People", "Person", "", "Person",
DisplayName = "Mutation name and description validation for plural entity name with singular defined")]
[DataRow(GraphQLTestHelpers.PERSON_GQL, "Person", null, null, "Person",
DisplayName = "Mutation name and description validation for singular entity name with singular plural not defined")]
[DataRow(GraphQLTestHelpers.PERSON_GQL, "Person", "Person", "People", "Person",
DisplayName = "Mutation name and description validation for singular entity name with singular plural defined")]
public void ValidateMutationsAreCreatedWithRightName(
string gql,
string entityName,
string singularName,
string pluralName,
string expectedName
)
{
DocumentNode root = Utf8GraphQLParser.Parse(gql);
Dictionary<string, EntityMetadata> entityPermissionsMap = GraphQLTestHelpers.CreateStubEntityPermissionsMap(
new string[] { entityName },
new Operation[] { Operation.Create, Operation.Update, Operation.Delete },
new string[] { "anonymous", "authenticated" });

Entity entity = (singularName is not null)
? GraphQLTestHelpers.GenerateEntityWithSingularPlural(singularName, pluralName)
: GraphQLTestHelpers.GenerateEmptyEntity();

DocumentNode mutationRoot = MutationBuilder.Build(
root,
DatabaseType.cosmos,
new Dictionary<string, Entity> { { entityName, entity } },
entityPermissionsMap: entityPermissionsMap
);

ObjectTypeDefinitionNode mutation = GetMutationNode(mutationRoot);
Assert.IsNotNull(mutation);

// The permissions are setup for create, update and delete operations.
// So create, update and delete mutations should get generated.
// A Check to validate that the count of mutations generated is 3.
Assert.AreEqual(3, mutation.Fields.Count);

// Name and Description validations for Create mutation
string expectedCreateMutationName = $"create{expectedName}";
string expectedCreateMutationDescription = $"Creates a new {expectedName}";
Assert.AreEqual(1, mutation.Fields.Count(f => f.Name.Value == expectedCreateMutationName));
FieldDefinitionNode createMutation = mutation.Fields.First(f => f.Name.Value == expectedCreateMutationName);
Assert.AreEqual(expectedCreateMutationDescription, createMutation.Description.Value);

// Name and Description validations for Update mutation
string expectedUpdateMutationName = $"update{expectedName}";
string expectedUpdateMutationDescription = $"Updates a {expectedName}";
Assert.AreEqual(1, mutation.Fields.Count(f => f.Name.Value == expectedUpdateMutationName));
FieldDefinitionNode updateMutation = mutation.Fields.First(f => f.Name.Value == expectedUpdateMutationName);
Assert.AreEqual(expectedUpdateMutationDescription, updateMutation.Description.Value);

// Name and Description validations for Delete mutation
string expectedDeleteMutationName = $"delete{expectedName}";
string expectedDeleteMutationDescription = $"Delete a {expectedName}";
Assert.AreEqual(1, mutation.Fields.Count(f => f.Name.Value == expectedDeleteMutationName));
FieldDefinitionNode deleteMutation = mutation.Fields.First(f => f.Name.Value == expectedDeleteMutationName);
Assert.AreEqual(expectedDeleteMutationDescription, deleteMutation.Description.Value);
}
}
}
Loading