diff --git a/Directory.Build.props b/Directory.Build.props index 9ecf5ea0c..b104e9f54 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.1.5 + 1.1.6 SimpleIdServer SimpleIdServer diff --git a/src/Scim/SimpleIdServer.Scim.Persistence.EF/Extensions/MappingExtensions.cs b/src/Scim/SimpleIdServer.Scim.Persistence.EF/Extensions/MappingExtensions.cs index 76181aba9..7e637b99a 100644 --- a/src/Scim/SimpleIdServer.Scim.Persistence.EF/Extensions/MappingExtensions.cs +++ b/src/Scim/SimpleIdServer.Scim.Persistence.EF/Extensions/MappingExtensions.cs @@ -148,7 +148,7 @@ public static SCIMRepresentation ToDomain(this SCIMRepresentationModel represent ResourceType = representation.ResourceType, Id = representation.Id, Schemas = representation.Schemas.Select(s => ToDomain(s.Schema)).ToList(), - Attributes = representation.Attributes.Select(s => + Attributes = representation.Attributes.Where(_ => _.Parent == null).Select(s => { return ToDomain(s); }).ToList() diff --git a/src/Scim/SimpleIdServer.Scim/Commands/Handlers/ReplaceRepresentationCommandHandler.cs b/src/Scim/SimpleIdServer.Scim/Commands/Handlers/ReplaceRepresentationCommandHandler.cs index 88e8e593a..6e57fa91d 100644 --- a/src/Scim/SimpleIdServer.Scim/Commands/Handlers/ReplaceRepresentationCommandHandler.cs +++ b/src/Scim/SimpleIdServer.Scim/Commands/Handlers/ReplaceRepresentationCommandHandler.cs @@ -67,6 +67,7 @@ public async Task Handle(ReplaceRepresentationCommand replac } } + existingRepresentation.SetExternalId(updatedRepresentation.ExternalId); existingRepresentation.SetUpdated(DateTime.UtcNow); using (var transaction = await _scimRepresentationCommandRepository.StartTransaction()) { diff --git a/src/Scim/SimpleIdServer.Scim/Domain/SCIMRepresentation.cs b/src/Scim/SimpleIdServer.Scim/Domain/SCIMRepresentation.cs index 62c4b2d45..5e2bbb400 100644 --- a/src/Scim/SimpleIdServer.Scim/Domain/SCIMRepresentation.cs +++ b/src/Scim/SimpleIdServer.Scim/Domain/SCIMRepresentation.cs @@ -70,6 +70,11 @@ public void SetCreated(DateTime created) Created = created; } + public void SetExternalId(string externalId) + { + ExternalId = externalId; + } + public void SetUpdated(DateTime lastModified) { LastModified = lastModified; diff --git a/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature b/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature index d770ea009..cf443cb12 100644 --- a/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature +++ b/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature @@ -195,14 +195,16 @@ Scenario: Check user can be updated (HTTP PUT) | name | { "formatted" : "formatted", "familyName": "familyName", "givenName": "givenName" } | | phones | [ { "phoneNumber": "01", "type": "mobile" }, { "phoneNumber": "02", "type": "home" } ] | | employeeNumber | number | + | externalId | ext | And extract JSON from body And extract 'id' from JSON body And execute HTTP PUT JSON request 'http://localhost/Users/$id$' - | Key | Value | - | schemas | [ "urn:ietf:params:scim:schemas:core:2.0:User" ] | - | name | { "formatted" : "newFormatted", "familyName": "newFamilyName", "givenName": "newGivenName" } | - | id | $id$ | + | Key | Value | + | schemas | [ "urn:ietf:params:scim:schemas:core:2.0:User" ] | + | name | { "formatted" : "newFormatted", "familyName": "newFamilyName", "givenName": "newGivenName" } | + | id | $id$ | + | externalId | newext | And execute HTTP GET request 'http://localhost/Users/$id$' And extract JSON from body @@ -212,6 +214,7 @@ Scenario: Check user can be updated (HTTP PUT) Then HTTP HEADER contains 'ETag' Then JSON 'schemas[0]'='urn:ietf:params:scim:schemas:core:2.0:User' Then JSON 'userName'='bjen' + Then JSON 'externalId'='newext' Then JSON 'name.formatted'='newFormatted' Then JSON 'name.familyName'='newFamilyName' Then JSON 'name.givenName'='newGivenName' diff --git a/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature.cs b/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature.cs index 3c7b0843f..427d273bd 100644 --- a/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature.cs +++ b/tests/SimpleIdServer.Scim.Host.Acceptance.Tests/Features/Users.feature.cs @@ -619,11 +619,14 @@ public virtual void CheckUserCanBeUpdatedHTTPPUT() table21.AddRow(new string[] { "employeeNumber", "number"}); + table21.AddRow(new string[] { + "externalId", + "ext"}); #line 191 testRunner.When("execute HTTP POST JSON request \'http://localhost/Users\'", ((string)(null)), table21, "When "); -#line 199 - testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line 200 + testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line 201 testRunner.And("extract \'id\' from JSON body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden TechTalk.SpecFlow.Table table22 = new TechTalk.SpecFlow.Table(new string[] { @@ -639,27 +642,32 @@ public virtual void CheckUserCanBeUpdatedHTTPPUT() table22.AddRow(new string[] { "id", "$id$"}); -#line 201 + table22.AddRow(new string[] { + "externalId", + "newext"}); +#line 202 testRunner.And("execute HTTP PUT JSON request \'http://localhost/Users/$id$\'", ((string)(null)), table22, "And "); -#line 207 +#line 209 testRunner.And("execute HTTP GET request \'http://localhost/Users/$id$\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 208 - testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line 210 + testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line 212 testRunner.Then("HTTP status code equals to \'200\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 211 +#line 213 testRunner.Then("HTTP HEADER contains \'Location\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 212 +#line 214 testRunner.Then("HTTP HEADER contains \'ETag\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 213 +#line 215 testRunner.Then("JSON \'schemas[0]\'=\'urn:ietf:params:scim:schemas:core:2.0:User\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 214 +#line 216 testRunner.Then("JSON \'userName\'=\'bjen\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 215 +#line 217 + testRunner.Then("JSON \'externalId\'=\'newext\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); +#line 218 testRunner.Then("JSON \'name.formatted\'=\'newFormatted\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 216 +#line 219 testRunner.Then("JSON \'name.familyName\'=\'newFamilyName\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 217 +#line 220 testRunner.Then("JSON \'name.givenName\'=\'newGivenName\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); @@ -671,7 +679,7 @@ public virtual void CheckUserCanBeUpdatedHTTPPUT() public virtual void CheckUserCanBePatchedHTTPPATCH() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Check user can be patched (HTTP PATCH)", null, ((string[])(null))); -#line 219 +#line 222 this.ScenarioInitialize(scenarioInfo); this.ScenarioStart(); #line hidden @@ -702,11 +710,11 @@ public virtual void CheckUserCanBePatchedHTTPPATCH() table23.AddRow(new string[] { "roles", "[ \"role1\", \"role2\" ]"}); -#line 220 +#line 223 testRunner.When("execute HTTP POST JSON request \'http://localhost/Users\'", ((string)(null)), table23, "When "); -#line 230 +#line 233 testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 231 +#line 234 testRunner.And("extract \'id\' from JSON body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line hidden TechTalk.SpecFlow.Table table24 = new TechTalk.SpecFlow.Table(new string[] { @@ -718,37 +726,37 @@ public virtual void CheckUserCanBePatchedHTTPPATCH() table24.AddRow(new string[] { "Operations", @"[ { ""op"" : ""remove"", ""path"": ""phones[phoneNumber eq 01]"" }, { ""op"": ""add"", ""path"": ""phones"", ""value"": { ""phoneNumber"": ""03"", ""type"": ""mobile"" } }, { ""op"": ""replace"", ""path"": ""userName"", ""value"": ""cassandra"" }, { ""op"" : ""remove"", ""path"": ""scores.math[score eq \""10\""]"" }, { ""op"" : ""add"", ""path"": ""scores.math"", ""value"": { ""score"": ""20"" } }, { ""op"": ""add"", ""path"": ""roles"", ""value"": ""role3"" } ]"}); -#line 232 +#line 235 testRunner.And("execute HTTP PATCH JSON request \'http://localhost/Users/$id$\'", ((string)(null)), table24, "And "); -#line 237 +#line 240 testRunner.And("execute HTTP GET request \'http://localhost/Users/$id$\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 238 +#line 241 testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 240 +#line 243 testRunner.Then("HTTP status code equals to \'200\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 241 +#line 244 testRunner.Then("HTTP HEADER contains \'Location\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 242 +#line 245 testRunner.Then("HTTP HEADER contains \'ETag\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 243 +#line 246 testRunner.Then("JSON \'schemas[0]\'=\'urn:ietf:params:scim:schemas:core:2.0:User\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 244 +#line 247 testRunner.Then("JSON \'userName\'=\'cassandra\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 245 +#line 248 testRunner.Then("JSON \'phones[0].phoneNumber\'=\'02\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 246 +#line 249 testRunner.Then("JSON \'phones[0].type\'=\'home\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 247 +#line 250 testRunner.Then("JSON \'phones[1].phoneNumber\'=\'03\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 248 +#line 251 testRunner.Then("JSON \'phones[1].type\'=\'mobile\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 249 +#line 252 testRunner.Then("JSON \'scores.math[0].score\'=\'20\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 250 +#line 253 testRunner.Then("JSON \'roles[0]\'=\'role1\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 251 +#line 254 testRunner.Then("JSON \'roles[1]\'=\'role2\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 252 +#line 255 testRunner.Then("JSON \'roles[2]\'=\'role3\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); @@ -760,7 +768,7 @@ public virtual void CheckUserCanBePatchedHTTPPATCH() public virtual void CheckNoUserIsReturnedWhenCountParameterIs0() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Check no user is returned when count parameter is 0", null, ((string[])(null))); -#line 254 +#line 257 this.ScenarioInitialize(scenarioInfo); this.ScenarioStart(); #line hidden @@ -791,21 +799,21 @@ public virtual void CheckNoUserIsReturnedWhenCountParameterIs0() table25.AddRow(new string[] { "roles", "[ \"role1\", \"role2\" ]"}); -#line 255 +#line 258 testRunner.When("execute HTTP POST JSON request \'http://localhost/Users\'", ((string)(null)), table25, "When "); -#line 265 +#line 268 testRunner.And("execute HTTP GET request \'http://localhost/Users?count=0\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 266 - testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); #line 269 + testRunner.And("extract JSON from body", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); +#line 272 testRunner.Then("HTTP status code equals to \'200\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 270 +#line 273 testRunner.Then("JSON \'schemas[0]\'=\'urn:ietf:params:scim:api:messages:2.0:ListResponse\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 271 +#line 274 testRunner.Then("JSON \'totalResults\'=\'1\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 272 +#line 275 testRunner.Then("JSON \'startIndex\'=\'1\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line 273 +#line 276 testRunner.Then("JSON \'itemsPerPage\'=\'0\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup();