diff --git a/backend/Omnikeeper/GraphQL/TraitEntities/TraitEntityTypes.cs b/backend/Omnikeeper/GraphQL/TraitEntities/TraitEntityTypes.cs index 342e4dfd..13f291ef 100644 --- a/backend/Omnikeeper/GraphQL/TraitEntities/TraitEntityTypes.cs +++ b/backend/Omnikeeper/GraphQL/TraitEntities/TraitEntityTypes.cs @@ -266,8 +266,11 @@ void Add(TraitAttribute ta, bool isRequired) return null; } + // ctx.FieldDefinition.Name is escaped (means "." is escaped as "__") + // while o.TraitAttributes is still unescaped var fn = ctx.FieldDefinition.Name; - if (o.TraitAttributes.TryGetValue(fn, out var v)) + var fnUnescaped = fn.Replace("__", "."); // HACK: this is not 100% failure proof as the actual escaping process does more than just escaping ".", see GenerateTraitAttributeFieldName() + if (o.TraitAttributes.TryGetValue(fnUnescaped, out var v)) { return v.Attribute.Value.ToGraphQLValue(); } diff --git a/backend/Tests/Integration/GraphQL/TraitEntityWithDotAttributeIdentifierTest.cs b/backend/Tests/Integration/GraphQL/TraitEntityWithDotAttributeIdentifierTest.cs new file mode 100644 index 00000000..e6e16fc5 --- /dev/null +++ b/backend/Tests/Integration/GraphQL/TraitEntityWithDotAttributeIdentifierTest.cs @@ -0,0 +1,128 @@ +using NUnit.Framework; +using Omnikeeper.Base.Entity; +using Omnikeeper.Base.Model; +using System.Threading.Tasks; +using Tests.Integration.GraphQL.Base; + +namespace Tests.Integration.GraphQL +{ + class TraitEntityWithDotAttributeIdentifierTest : QueryTestBase + { + [Test] + public async Task TestBasics() + { + var userInDatabase = await SetupDefaultUser(); + var (layerOkConfig, _) = await GetService().CreateLayerIfNotExists("__okconfig", ModelContextBuilder.BuildImmediate()); + var (layer1, _) = await GetService().CreateLayerIfNotExists("layer_1", ModelContextBuilder.BuildImmediate()); + var user = new AuthenticatedInternalUser(userInDatabase); + + // force rebuild graphql schema + await ReinitSchema(); + + string mutationCreateTrait = @" +mutation { + manage_upsertRecursiveTrait( + trait: { + id: ""test_trait_a"" + requiredAttributes: [ + { + identifier: ""test.id"" + template: { + name: ""test_trait_a.id"" + type: TEXT + isID: true + isArray: false + valueConstraints: [] + } + } + ] + optionalAttributes: [] + optionalRelations: [], + requiredTraits: [] + } + ) { + id + } +} +"; + var expected1 = @" +{ + ""manage_upsertRecursiveTrait"": + { + ""id"": ""test_trait_a"" + } +}"; + AssertQuerySuccess(mutationCreateTrait, expected1, user); + + // force rebuild graphql schema + await ReinitSchema(); + + var queryTestTraitA = @" +{ + traitEntities(layers: [""layer_1""]) { + test_trait_a { + all { + entity { + test__id + } + } + } + } + } +"; + var expected2 = @" +{ + ""traitEntities"": { + ""test_trait_a"": { + ""all"": [] + } + } +} +"; + AssertQuerySuccess(queryTestTraitA, expected2, user); + + // NOTE the double underscore intstead of a dot in the query + var mutationInsert = @" +mutation { + insertNew_test_trait_a( + layers: [""layer_1""] + writeLayer: ""layer_1"" + ciName: ""Entity 1"" + input: { test__id: ""entity_1"" } + ) { + entity { test__id } + } + } +"; + + var expected3 = @" +{ + ""insertNew_test_trait_a"": { + ""entity"": { + ""test__id"": ""entity_1"" + } + } + } +"; + AssertQuerySuccess(mutationInsert, expected3, user); + + + var expected4 = @" +{ + ""traitEntities"": { + ""test_trait_a"": { + ""all"": [ + { + ""entity"": { + ""test__id"": ""entity_1"" + } + } + ] + } + } +} +"; + AssertQuerySuccess(queryTestTraitA, expected4, user); + } + } +}