From 3932e87e4c2dd6b0c2bae9436d2d5bd4503b993b Mon Sep 17 00:00:00 2001 From: a921966 Date: Fri, 21 Mar 2025 13:16:24 +0100 Subject: [PATCH 1/3] Feature - support for HTTP PATCH request in client Prism implementation --- .../client/api/ObjectModifyService.java | 5 +- .../impl/prism/RestPrismObjectService.java | 2 +- .../client/impl/prism/RestPrismService.java | 96 +++++++++++++++---- .../impl/prism/TestRestPrismService.java | 43 ++++++++- .../restjaxb/RestJaxbObjectModifyService.java | 8 +- 5 files changed, 127 insertions(+), 27 deletions(-) diff --git a/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/ObjectModifyService.java b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/ObjectModifyService.java index 63a4310..a57533a 100644 --- a/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/ObjectModifyService.java +++ b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/ObjectModifyService.java @@ -15,10 +15,10 @@ */ package com.evolveum.midpoint.client.api; -import java.util.List; +import java.io.InputStream; import java.util.Map; -import com.evolveum.midpoint.client.api.exception.CommonException; +import com.evolveum.midpoint.client.api.exception.SchemaException; import com.evolveum.midpoint.client.api.verb.Post; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; @@ -32,6 +32,7 @@ public interface ObjectModifyService extends Post replace(Map modifications); ObjectModifyService delete(String path, Object value); ObjectModifyService delete(Map modifications); + ObjectModifyService setModifications(InputStream inputStream) throws SchemaException; ExecuteOptionSupport.WithPost> options(); diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectService.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectService.java index 5a7dc62..9177167 100644 --- a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectService.java +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectService.java @@ -52,7 +52,7 @@ public O get(List options, List include, List exclude) t @Override public ObjectModifyService modify() throws ObjectNotFoundException, AuthenticationException { - throw new UnsupportedOperationException("Not impelemted yet"); + return new RestPrismObjectModifyService<>(getService(), getType(), getOid()); } @Override diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismService.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismService.java index 74dc96f..26e954f 100644 --- a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismService.java +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismService.java @@ -16,8 +16,10 @@ package com.evolveum.midpoint.client.impl.prism; import java.io.IOException; +import java.io.InputStream; import java.util.List; +import jakarta.ws.rs.BadRequestException; import org.apache.commons.lang3.StringUtils; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.client5.http.fluent.Response; @@ -120,6 +122,14 @@ O parseObject(HttpEntity httpEntity) throws SchemaExcepti return object.asObjectable(); } + Object parseObjectModification(InputStream inputStream) throws SchemaException { + try { + return prismContext.parserFor(inputStream).json().parseRealValue(); + } catch (com.evolveum.midpoint.util.exception.SchemaException | IOException e) { + throw new SchemaException(e.getMessage(), e); + } + } + OperationResult getOperationResult(HttpEntity httpEntity) throws SchemaException { OperationResultType result = parseOperationResult(httpEntity); return OperationResult.createOperationResult(result); @@ -156,15 +166,7 @@ ClassicHttpResponse httpPost(String relativePath, HttpEntity object) throws Sche ClassicHttpResponse httpPost(String relativePath, HttpEntity object, List options) throws SchemaException { StringBuilder uri = new StringBuilder(baseUrl + "/" + relativePath); - if (options != null && options.size() > 0) { - uri.append("?options="); - for (String option : options) { - if (options.indexOf(option) > 0) { - uri.append(","); - } - uri.append(option); - } - } + appendOptionsToUri(options, uri); Request req = Request.post(uri.toString()); if (object != null) { req.body(object); @@ -176,18 +178,25 @@ ClassicHttpResponse httpPost(String relativePath, HttpEntity object, List options, String oid) throws SchemaException { + StringBuilder uri = new StringBuilder(baseUrl + "/" + relativePath + "/" + oid); + appendOptionsToUri(options, uri); + Request req = Request.patch(uri.toString()); + if (object != null) { + req.body(object); + } + + try { + return (ClassicHttpResponse) req.execute(httpClient).returnResponse(); + } catch (IOException e) { + throw new SchemaException(e.getMessage(), e); + } + } + Response httpDelete(String fullPath, List options) { StringBuilder uri = new StringBuilder(fullPath); - if (options != null && options.size() > 0) { - uri.append("?options="); - for (String option : options) { - if (options.indexOf(option) > 0) { - uri.append(","); - } - uri.append(option); - } - } + appendOptionsToUri(options, uri); Request req = Request.delete(uri.toString()); try { return req.execute(httpClient); @@ -221,6 +230,55 @@ String addObject(ObjectTypes type, ObjectType object, List options) thro } } + String modifyObject(ObjectTypes type, O object, List options, String oid) throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { + ClassicHttpResponse httpResponse = httpPatch(type.getRestType(), createEntity(object), options, oid); + + OperationResult result = null; + return switch (httpResponse.getCode()) { + case HttpStatus.SC_NO_CONTENT, 240 -> getOidOrDefault(httpResponse, oid); + case 250 -> throw new PartialErrorException(getOperationResult(httpResponse.getEntity()).getMessage()); + case HttpStatus.SC_CONFLICT -> { + result = getOperationResult(httpResponse.getEntity()); + throw new ObjectAlreadyExistsException(result.getMessage()); + } + case HttpStatus.SC_FORBIDDEN -> { + result = getOperationResult(httpResponse.getEntity()); + throw new SecurityViolationException(result.getMessage()); + } + case HttpStatus.SC_UNAUTHORIZED -> { + result = getOperationResult(httpResponse.getEntity()); + throw new AuthenticationException(result.getMessage()); + } + case HttpStatus.SC_BAD_REQUEST -> { + result = getOperationResult(httpResponse.getEntity()); + throw new BadRequestException(result.getMessage()); + } + case HttpStatus.SC_NOT_FOUND -> { + result = getOperationResult(httpResponse.getEntity()); + throw new ObjectNotFoundException(result.getMessage()); + } + case HttpStatus.SC_SERVER_ERROR -> { + result = getOperationResult(httpResponse.getEntity()); + throw new InternalServerErrorException(result.getMessage()); + } + default -> throw new UnsupportedOperationException("Not implemented yet: " + httpResponse); + }; + } + + private String getOidOrDefault(ClassicHttpResponse response, String oidReq) { + try { + return getOidFromLocation(response); + } catch (ProtocolException | IllegalStateException e) { + return oidReq; + } + } + + private void appendOptionsToUri(List options, StringBuilder uri) { + if (options != null && !options.isEmpty()) { + uri.append("?options=").append(String.join(",", options)); + } + } + String getOidFromLocation(HttpResponse httpResponse) throws ProtocolException { Header header = httpResponse.getHeader("Location"); if (header == null) { @@ -233,7 +291,7 @@ String getOidFromLocation(HttpResponse httpResponse) throws ProtocolException { return null; } - private StringEntity createEntity(O object) throws SchemaException { + private StringEntity createEntity(O object) throws SchemaException { try { return new StringEntity(prismContext.xmlSerializer().serializeRealValue(object), ContentType.APPLICATION_XML); } catch (com.evolveum.midpoint.util.exception.SchemaException e) { diff --git a/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java b/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java index 5754177..1c3df51 100644 --- a/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java +++ b/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java @@ -15,7 +15,9 @@ */ package com.evolveum.midpoint.client.impl.prism; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; import org.testng.Assert; import org.testng.AssertJUnit; @@ -27,19 +29,32 @@ import com.evolveum.midpoint.client.api.Service; import com.evolveum.midpoint.client.api.exception.ObjectNotFoundException; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; public class TestRestPrismService { + private static final String DATA_DIR = "src/test/resources/request"; private Service service; + private String testUserOid; + @BeforeClass public void init() throws Exception { service = createClient(); + + //Create a user to later use in tests, optionally set other fields... + UserType testUser = new UserType(); + testUser.setName(new PolyStringType("testUser")); + ObjectReference ref = service.users().add(testUser).post(); + Assert.assertNotNull(ref.getOid(), "User OID must not be null after creation."); + testUserOid = ref.getOid(); } @AfterClass - public void shutdown() { + public void shutdown() throws ObjectNotFoundException { + service.users().oid(testUserOid).delete(); service.close(); } @@ -51,7 +66,6 @@ public void test001getUser() throws Exception { System.out.println("User : " + user.getName()); AssertJUnit.assertEquals("administrator", user.getName().getOrig()); - } @Test @@ -101,6 +115,29 @@ public void test019deleteUser() throws Exception { } + /** + * Calls the modify service using setModifications(InputStream) + * and verifies that the returned OID matches the test user’s OID. (HTTP PATCH) + * Org in filter can be found here in overlay example project https://github.com/Evolveum/midpoint-overlay-example/blob/master/src/main/resources/initial-objects/920-org-root.xml + */ + @Test + public void test030ModifyUser() throws Exception { + File file = new File(DATA_DIR, "user-patch-delta.json"); + + try (InputStream inputStream = new FileInputStream(file)) { + // Call the modify service using setModifications(inputStream) and then post the changes. + ObjectReference userRef = service.users().oid(testUserOid) + .modify() + .setModifications(inputStream) + .post(); + + AssertJUnit.assertNotNull(userRef.getOid()); + Assert.assertEquals(userRef.getOid(), testUserOid, "Modified OID should match the test user's OID."); + } + } + + + @Test public void test100testResource() throws Exception { diff --git a/midpoint-client-impl-rest-jaxb/src/main/java/com/evolveum/midpoint/client/impl/restjaxb/RestJaxbObjectModifyService.java b/midpoint-client-impl-rest-jaxb/src/main/java/com/evolveum/midpoint/client/impl/restjaxb/RestJaxbObjectModifyService.java index 42c586d..2131a2c 100644 --- a/midpoint-client-impl-rest-jaxb/src/main/java/com/evolveum/midpoint/client/impl/restjaxb/RestJaxbObjectModifyService.java +++ b/midpoint-client-impl-rest-jaxb/src/main/java/com/evolveum/midpoint/client/impl/restjaxb/RestJaxbObjectModifyService.java @@ -16,7 +16,7 @@ package com.evolveum.midpoint.client.impl.restjaxb; import java.util.ArrayList; -import java.util.HashMap; +import java.io.InputStream; import java.util.List; import java.util.Map; @@ -24,7 +24,6 @@ import com.evolveum.midpoint.client.api.exception.*; -import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.core.Response; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; @@ -88,6 +87,11 @@ public RestJaxbObjectModifyService delete(Map modifications){ return this; } + @Override + public RestJaxbObjectModifyService setModifications(InputStream inputStream) throws SchemaException { + throw new UnsupportedOperationException("Not implemented yet"); + } + private void addModification(String path, Object value, ModificationTypeType modificationType){ modifications.add(RestUtil.buildItemDelta(modificationType, path, value)); } From ac7ec9c5499b2e2ef9db0e72130c7bf64f87a171 Mon Sep 17 00:00:00 2001 From: a921966 Date: Fri, 21 Mar 2025 13:17:26 +0100 Subject: [PATCH 2/3] Feature - support for HTTP PATCH request in client Prism implementation --- .../prism/RestPrismObjectModifyService.java | 128 ++++++++++++++++++ .../client/impl/prism/RestPrismUtils.java | 58 ++++++++ .../resources/request/user-patch-delta.json | 18 +++ 3 files changed, 204 insertions(+) create mode 100644 midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java create mode 100644 midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismUtils.java create mode 100644 midpoint-client-impl-prism/src/test/resources/request/user-patch-delta.json diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java new file mode 100644 index 0000000..34ab536 --- /dev/null +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2017-2025 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.client.impl.prism; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.evolveum.midpoint.client.api.*; +import com.evolveum.midpoint.client.api.exception.CommonException; +import com.evolveum.midpoint.client.api.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.client.api.exception.ObjectNotFoundException; +import com.evolveum.midpoint.client.api.exception.SchemaException; +import com.evolveum.midpoint.schema.constants.ObjectTypes; +import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType; +import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType; + +/** + * @author Z14tk0 + */ +public class RestPrismObjectModifyService implements ObjectModifyService { + + private List modifications; + private final RestPrismService service; + private final ObjectTypes type; + private List options; + private String oid; + + @Override + public ExecuteOptionSupport.WithPost> options() { + return new ExecuteOptionsBuilder.WithPost>() { + @Override + public TaskFuture> apost() throws CommonException { + setOptions(this.optionsAsStringList()); + return RestPrismObjectModifyService.this.apost(); + } + }; + } + + public RestPrismObjectModifyService(RestPrismService service, ObjectTypes type, String oid) { + this.service = service; + this.type = type; + this.oid = oid; + this.modifications = new ArrayList<>(); + } + + public RestPrismObjectModifyService setOptions(List options) { + this.options = options; + return this; + } + + @Override + public RestPrismObjectModifyService add(String path, Object value){ + addModification(path, value, ModificationTypeType.ADD); + return this; + } + + @Override + public RestPrismObjectModifyService add(Map modifications){ + addModifications(modifications, ModificationTypeType.ADD); + return this; + } + + @Override + public RestPrismObjectModifyService replace(String path, Object value){ + addModification(path, value, ModificationTypeType.REPLACE); + return this; + } + + @Override + public RestPrismObjectModifyService replace(Map modifications){ + addModifications(modifications, ModificationTypeType.REPLACE); + return this; + } + + @Override + public RestPrismObjectModifyService delete(String path, Object value){ + addModification(path, value, ModificationTypeType.DELETE); + return this; + } + + @Override + public RestPrismObjectModifyService delete(Map modifications){ + addModifications(modifications, ModificationTypeType.DELETE); + return this; + } + + @Override + public RestPrismObjectModifyService setModifications(InputStream inputStream) throws SchemaException { + this.modifications.addAll(((ObjectModificationType) service.parseObjectModification(inputStream)).getItemDelta()); + return this; + } + + private void addModification(String path, Object value, ModificationTypeType modificationType){ + modifications.add(RestPrismUtils.buildItemDelta(modificationType, path, value)); + } + + private void addModifications(Map modifications, ModificationTypeType modificationType){ + modifications.forEach((path, value) -> + addModification(path, value, modificationType)); + } + + @Override + public TaskFuture> apost() throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { + + String oidRes = service.modifyObject(type, RestPrismUtils.buildModifyObject(modifications), options, oid); + + RestPrismObjectReference ref = new RestPrismObjectReference<>(oidRes, type.getClassDefinition()); + return new RestPrismCompletedFuture<>(ref); + } + +} diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismUtils.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismUtils.java new file mode 100644 index 0000000..7cdf700 --- /dev/null +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismUtils.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2025 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.client.impl.prism; + +import java.util.List; + +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectModificationType; +import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; +import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType; +import com.evolveum.prism.xml.ns._public.types_3.RawType; + +/** + * @author Z14tk0 + */ +public final class RestPrismUtils { + + private RestPrismUtils() { + throw new UnsupportedOperationException("Class cannot be instantiated."); + } + + public static ObjectModificationType buildModifyObject(List itemDeltas) { + ObjectModificationType objectModificationType = new ObjectModificationType(); + itemDeltas.forEach(itemDelta -> objectModificationType.getItemDelta().add(itemDelta)); + + return objectModificationType; + } + + public static ItemDeltaType buildItemDelta(ModificationTypeType modificationType, String path, Object value) { + //Create ItemDelta + ItemDeltaType itemDeltaType = new ItemDeltaType(); + itemDeltaType.setModificationType(modificationType); + + //Set Path + ItemPathType itemPathType = new ItemPathType(ItemPath.fromString(path)); + itemDeltaType.setPath(itemPathType); + + if (value != null) { + itemDeltaType.getValue().add(RawType.fromPropertyRealValue(value, null)); //TODO recheck if this works in all cases? + } + + return itemDeltaType; + } +} diff --git a/midpoint-client-impl-prism/src/test/resources/request/user-patch-delta.json b/midpoint-client-impl-prism/src/test/resources/request/user-patch-delta.json new file mode 100644 index 0000000..92e273e --- /dev/null +++ b/midpoint-client-impl-prism/src/test/resources/request/user-patch-delta.json @@ -0,0 +1,18 @@ +{ + "objectModification": { + "itemDelta": [ + { + "modificationType": "add", + "path": "assignment", + "value": [ + { + "targetRef": { + "filter": { "text": ". inOid (\"258cfacc-4705-11e8-b772-874c20402410\")" }, + "type": "c:OrgType" + } + } + ] + } + ] + } +} \ No newline at end of file From d4cccf19a44214f468e30902c6bf8c6c62c63455 Mon Sep 17 00:00:00 2001 From: a921966 Date: Mon, 24 Mar 2025 16:20:47 +0100 Subject: [PATCH 3/3] - added one more test for modify/patch operation - code restructuring and minor cleanup --- .../client/api/AbstractObjectWebResource.java | 42 +++++++++++++++++++ .../client/api/AbstractWebResource.java | 35 ++++++++++++++++ .../prism/RestPrismObjectModifyService.java | 15 +++---- .../impl/prism/RestPrismObjectReference.java | 24 ++++++++--- .../impl/prism/TestRestPrismService.java | 39 ++++++++++++++--- 5 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractObjectWebResource.java create mode 100644 midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractWebResource.java diff --git a/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractObjectWebResource.java b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractObjectWebResource.java new file mode 100644 index 0000000..e3d5178 --- /dev/null +++ b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractObjectWebResource.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2025 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.client.api; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +/** + * @author Z14tk0 + * + */ +public abstract class AbstractObjectWebResource extends AbstractWebResource { + + private final Class type; + private final String oid; + + public AbstractObjectWebResource(final Service service, final Class type, final String oid) { + super(service); + this.type = type; + this.oid = oid; + } + + protected Class getType() { + return type; + } + + protected String getOid() { + return oid; + } +} diff --git a/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractWebResource.java b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractWebResource.java new file mode 100644 index 0000000..e8cabfd --- /dev/null +++ b/midpoint-client-api/src/main/java/com/evolveum/midpoint/client/api/AbstractWebResource.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2025 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.client.api; + +/** + * @author Z14tk0 + * + */ +public abstract class AbstractWebResource { + + private final Service service; + + public AbstractWebResource(final Service service) { + super(); + this.service = service; + } + + protected Service getService() { + return service; + } + +} diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java index 34ab536..eeec9c0 100644 --- a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectModifyService.java @@ -34,13 +34,10 @@ /** * @author Z14tk0 */ -public class RestPrismObjectModifyService implements ObjectModifyService { +public class RestPrismObjectModifyService extends AbstractObjectWebResource implements ObjectModifyService { private List modifications; - private final RestPrismService service; - private final ObjectTypes type; private List options; - private String oid; @Override public ExecuteOptionSupport.WithPost> options() { @@ -54,9 +51,7 @@ public TaskFuture> apost() throws CommonException { } public RestPrismObjectModifyService(RestPrismService service, ObjectTypes type, String oid) { - this.service = service; - this.type = type; - this.oid = oid; + super(service, type.getClassDefinition(), oid); this.modifications = new ArrayList<>(); } @@ -103,7 +98,7 @@ public RestPrismObjectModifyService delete(Map modifications) @Override public RestPrismObjectModifyService setModifications(InputStream inputStream) throws SchemaException { - this.modifications.addAll(((ObjectModificationType) service.parseObjectModification(inputStream)).getItemDelta()); + this.modifications.addAll(((ObjectModificationType) ((RestPrismService) getService()).parseObjectModification(inputStream)).getItemDelta()); return this; } @@ -119,9 +114,9 @@ private void addModifications(Map modifications, ModificationTyp @Override public TaskFuture> apost() throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { - String oidRes = service.modifyObject(type, RestPrismUtils.buildModifyObject(modifications), options, oid); + String oidRes = ((RestPrismService) getService()).modifyObject(ObjectTypes.getObjectType(getType()), RestPrismUtils.buildModifyObject(modifications), options, getOid()); - RestPrismObjectReference ref = new RestPrismObjectReference<>(oidRes, type.getClassDefinition()); + RestPrismObjectReference ref = new RestPrismObjectReference<>((RestPrismService) getService(), oidRes, getType()); return new RestPrismCompletedFuture<>(ref); } diff --git a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectReference.java b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectReference.java index 40e32d9..b5ea21e 100644 --- a/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectReference.java +++ b/midpoint-client-impl-prism/src/main/java/com/evolveum/midpoint/client/impl/prism/RestPrismObjectReference.java @@ -15,16 +15,27 @@ */ package com.evolveum.midpoint.client.impl.prism; +import com.evolveum.midpoint.client.api.AbstractObjectWebResource; import com.evolveum.midpoint.client.api.ObjectReference; import com.evolveum.midpoint.client.api.exception.ObjectNotFoundException; +import com.evolveum.midpoint.client.api.exception.SchemaException; +import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -public class RestPrismObjectReference implements ObjectReference { +public class RestPrismObjectReference extends AbstractObjectWebResource implements ObjectReference { private String oid; private Class type; + private O object = null; - public RestPrismObjectReference(String oid, Class type) { + public RestPrismObjectReference(String oid, Class type) { + super(null, type, oid); + this.oid = oid; + this.type = type; + } + + public RestPrismObjectReference(RestPrismService service, String oid, Class type) { + super(service, type, oid); this.oid = oid; this.type = type; } @@ -41,7 +52,7 @@ public Class getType() { @Override public O getObject() throws ObjectNotFoundException { - return null; + return object; } @Override @@ -50,7 +61,10 @@ public boolean containsObject() { } @Override - public O get() throws ObjectNotFoundException { - return null; + public O get() throws ObjectNotFoundException, SchemaException { + if (object == null) { + object = ((RestPrismService) getService()).getObject(ObjectTypes.getObjectType(getType()), getOid()); + } + return object; } } diff --git a/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java b/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java index 1c3df51..b2d2edd 100644 --- a/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java +++ b/midpoint-client-impl-prism/src/test/java/com/evolveum/midpoint/client/impl/prism/TestRestPrismService.java @@ -18,8 +18,9 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; -import org.testng.Assert; import org.testng.AssertJUnit; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -32,6 +33,8 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; +import static org.testng.AssertJUnit.*; + public class TestRestPrismService { private static final String DATA_DIR = "src/test/resources/request"; @@ -39,7 +42,6 @@ public class TestRestPrismService { private Service service; private String testUserOid; - @BeforeClass public void init() throws Exception { service = createClient(); @@ -48,7 +50,7 @@ public void init() throws Exception { UserType testUser = new UserType(); testUser.setName(new PolyStringType("testUser")); ObjectReference ref = service.users().add(testUser).post(); - Assert.assertNotNull(ref.getOid(), "User OID must not be null after creation."); + assertNotNull(ref.getOid()); testUserOid = ref.getOid(); } @@ -118,7 +120,7 @@ public void test019deleteUser() throws Exception { /** * Calls the modify service using setModifications(InputStream) * and verifies that the returned OID matches the test user’s OID. (HTTP PATCH) - * Org in filter can be found here in overlay example project https://github.com/Evolveum/midpoint-overlay-example/blob/master/src/main/resources/initial-objects/920-org-root.xml + * Org in filter can be found in overlay example project https://github.com/Evolveum/midpoint-overlay-example/blob/master/src/main/resources/initial-objects/920-org-root.xml */ @Test public void test030ModifyUser() throws Exception { @@ -131,9 +133,34 @@ public void test030ModifyUser() throws Exception { .setModifications(inputStream) .post(); - AssertJUnit.assertNotNull(userRef.getOid()); - Assert.assertEquals(userRef.getOid(), testUserOid, "Modified OID should match the test user's OID."); + assertNotNull(userRef.getOid()); + assertEquals("Modified OID should match the test user's OID.", testUserOid, userRef.getOid()); + } + } + + @Test + public void test031ModifyUser() throws Exception { + Map modifications = new HashMap<>(); + modifications.put("description", "test description"); + + ObjectReference ref = null; + + try { + ref = service.users().oid(testUserOid) + .modify() + .replace(modifications) + .add("givenName", "Charlie") + .post(); + } catch (ObjectNotFoundException e) { + fail("Cannot modify user, user not found"); } + + UserType user = ref.get(); + assertEquals("test description", user.getDescription()); + assertEquals("Charlie", user.getGivenName().getOrig()); + ref = service.users().oid(testUserOid).modify().delete("givenName", "Charlie").post(); + + assertNull(ref.get().getGivenName()); }