From a9944e30a11619bfc1bd1f831ddc3894ff246262 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 4 Jan 2024 13:03:54 -0800 Subject: [PATCH] Call the history API for inbound orders (#746) * Changed OrderSender.sendOrder to return the sent submissionId (as optional) * Added sentSubmissionId to PartnerMetadata record, and renamed uniqueId to receivedSubmissionId. Also, overloaded record constructor to only take receivedSubmissionId * Updated tests * Added missed import and fixed more tests * Fixed test * Fixed e2e test * Refactored to split RS endpoint logic to its own ReportStreamEndpointClient class * Added custom exception for ReportStreamEndpointClient * Refactored for separation of concerns and clarity * Further refactoring in preparation to call the history API * Added implementation for http get method * Added call to history API to update receiver metadata * Added missing application context registration for ReportStreamEndpointClient * Removed unused line * Import missing class * Added some comments * Added methods to partially update PartnerMetadata and making sure to read existing metadata before updating * Added tests for PartnerMetadataOrchestrator * Some code cleaning * Fixed test * Added tests for PartnerMetadata changes * Added tests for requestHistoryEndpoint + some cleanup * Cleaned up naming and add logging * Implemented updateMetadataForReceivedOrder and getMetadata * Refactored PartnerMetadata.withSentSubmissionFields to split into separate methods for each parameter * Changed logic to make sure to save sentSubmissionId into metadata file even if not able to retrieve receiver * Fixed tests * Added tests for updateMetadataForReceivedOrder + validation logic * Added test to cover logic for missing receiver in getMetadata * Added todo comment * Added logging * Refactored string comparison * Made improvements based on Jeff's feedback * Simplified logic by using try/catch instead of checking for expected format * Added null check * Simplified logic by catching exception instead of checking for expected format * Return if sentSubmissionId is null given we can't update the metadata * Fixed wrong commit * Fixed failing tests * Update etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java Co-authored-by: halprin * Fixed exception * test case for single param constructor * single param contructor for PartnerMetadataException * super with sigle param * Added missing mock return value for requestHistoryEndpoint * Fixed failing test * Refactored to pass order hash as a parameter * PartnerMetadataOrchestrator unit test for updateMetadataForSentOrder when sentSubmissionId is null --------- Co-authored-by: halprin Co-authored-by: jorge Lopez --- .../e2e/MetadataTest.groovy | 1 - .../metadata/PartnerMetadataException.java | 4 + .../metadata/PartnerMetadataOrchestrator.java | 82 ++++++-- .../etor/orders/SendOrderUseCase.java | 4 +- .../localfile/MockRSEndpointClient.java | 2 + .../PartnerMetadataExceptionTest.groovy | 14 +- .../PartnerMetadataOrchestratorTest.groovy | 194 +++++++++++++++--- 7 files changed, 243 insertions(+), 58 deletions(-) diff --git a/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy b/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy index f37f9d9b0..5727426ad 100644 --- a/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy +++ b/e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/e2e/MetadataTest.groovy @@ -21,7 +21,6 @@ class MetadataTest extends Specification { def labOrderJsonFileString = Files.readString(Path.of("../examples/fhir/MN NBS FHIR Order Message.json")) when: - def orderResponse = orderClient.submit(labOrderJsonFileString, submissionId, true) then: diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataException.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataException.java index 5abed8c11..eac65c323 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataException.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataException.java @@ -6,4 +6,8 @@ public class PartnerMetadataException extends Exception { public PartnerMetadataException(String message, Throwable cause) { super(message, cause); } + + public PartnerMetadataException(String message) { + super(message); + } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java index fc12e23c7..5de7acaf7 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestrator.java @@ -1,8 +1,8 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata; import gov.hhs.cdc.trustedintermediary.etor.RSEndpointClient; -import gov.hhs.cdc.trustedintermediary.etor.orders.Order; import gov.hhs.cdc.trustedintermediary.external.reportstream.ReportStreamEndpointClientException; +import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter; import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException; import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference; @@ -24,6 +24,7 @@ public class PartnerMetadataOrchestrator { @Inject PartnerMetadataStorage partnerMetadataStorage; @Inject RSEndpointClient rsclient; @Inject Formatter formatter; + @Inject Logger logger; public static PartnerMetadataOrchestrator getInstance() { return INSTANCE; @@ -31,19 +32,38 @@ public static PartnerMetadataOrchestrator getInstance() { private PartnerMetadataOrchestrator() {} - public void updateMetadataForReceivedOrder(String receivedSubmissionId, Order order) + public void updateMetadataForReceivedOrder(String receivedSubmissionId, String orderHash) throws PartnerMetadataException { - // will call the RS history API given the submissionId, currently blocked by: - // https://github.com/CDCgov/prime-reportstream/issues/12624 - // from the response, extract the "sender" and "timestamp" (timeReceived) - // we will calculate the hash. - // then we call the metadata storage to save this stuff. - - String sender = "unknown"; - Instant timeReceived = Instant.now(); - String hash = String.valueOf(order.hashCode()); + // currently blocked by: https://github.com/CDCgov/prime-reportstream/issues/12624 + // once we get the right receivedSubmissionId from RS, this method should work + logger.logInfo( + "Looking up sender name and timeReceived from RS history API for receivedSubmissionId: {}", + receivedSubmissionId); + + String sender; + Instant timeReceived; + try { + String bearerToken = rsclient.getRsToken(); + String responseBody = + rsclient.requestHistoryEndpoint(receivedSubmissionId, bearerToken); + Map responseObject = + formatter.convertJsonToObject(responseBody, new TypeReference<>() {}); + + sender = responseObject.get("sender").toString(); + String timestamp = responseObject.get("timestamp").toString(); + timeReceived = Instant.parse(timestamp); + + } catch (Exception e) { + throw new PartnerMetadataException( + "Unable to retrieve metadata from RS history API", e); + } + + logger.logInfo( + "Updating metadata with sender: {}, timeReceived: {} and hash", + sender, + timeReceived); PartnerMetadata partnerMetadata = - new PartnerMetadata(receivedSubmissionId, sender, timeReceived, hash); + new PartnerMetadata(receivedSubmissionId, sender, timeReceived, orderHash); partnerMetadataStorage.saveMetadata(partnerMetadata); } @@ -54,13 +74,24 @@ public void updateMetadataForSentOrder(String receivedSubmissionId, String sentS return; } - PartnerMetadata partnerMetadata = - partnerMetadataStorage.readMetadata(receivedSubmissionId).orElseThrow(); + Optional optionalPartnerMetadata = + partnerMetadataStorage.readMetadata(receivedSubmissionId); + if (optionalPartnerMetadata.isEmpty()) { + logger.logWarning( + "Metadata not found for receivedSubmissionId: {}", receivedSubmissionId); + return; + } + + PartnerMetadata partnerMetadata = optionalPartnerMetadata.get(); if (!sentSubmissionId.equals(partnerMetadata.sentSubmissionId())) { + logger.logInfo("Updating metadata with sentSubmissionId: {}", sentSubmissionId); partnerMetadata = partnerMetadata.withSentSubmissionId(sentSubmissionId); partnerMetadataStorage.saveMetadata(partnerMetadata); } + logger.logInfo( + "Looking up receiver name from RS history API for sentSubmissionId: {}", + sentSubmissionId); String receiver; try { String bearerToken = rsclient.getRsToken(); @@ -71,19 +102,28 @@ public void updateMetadataForSentOrder(String receivedSubmissionId, String sentS "Unable to retrieve metadata from RS history API", e); } + logger.logInfo("Updating metadata with receiver: {}", receiver); partnerMetadata = partnerMetadata.withReceiver(receiver); partnerMetadataStorage.saveMetadata(partnerMetadata); } public Optional getMetadata(String receivedSubmissionId) throws PartnerMetadataException { - // call the metadata storage to get the metadata. - // check if the receiver is filled out, and if it isn't, call the RS history API to get the - // receiver. - // if had to call the history API, extract the receiver and call the metadata storage to - // save the metadata with the receiver added. - // return the metadata. - return partnerMetadataStorage.readMetadata(receivedSubmissionId); + Optional optionalPartnerMetadata = + partnerMetadataStorage.readMetadata(receivedSubmissionId); + if (optionalPartnerMetadata.isEmpty()) { + logger.logInfo("Metadata not found for receivedSubmissionId: {}", receivedSubmissionId); + return Optional.empty(); + } + + PartnerMetadata partnerMetadata = optionalPartnerMetadata.get(); + if (partnerMetadata.receiver() == null && partnerMetadata.sentSubmissionId() != null) { + logger.logInfo("Receiver name not found in metadata, looking up from RS history API"); + updateMetadataForSentOrder(receivedSubmissionId, partnerMetadata.sentSubmissionId()); + return partnerMetadataStorage.readMetadata(receivedSubmissionId); + } + + return Optional.of(partnerMetadata); } String getReceiverName(String responseBody) throws FormatterProcessingException { diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/SendOrderUseCase.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/SendOrderUseCase.java index 77b0a3986..c713efefd 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/SendOrderUseCase.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/orders/SendOrderUseCase.java @@ -47,7 +47,9 @@ private void savePartnerMetadataForReceivedOrder( } try { - partnerMetadataOrchestrator.updateMetadataForReceivedOrder(receivedSubmissionId, order); + String orderHash = String.valueOf(order.hashCode()); + partnerMetadataOrchestrator.updateMetadataForReceivedOrder( + receivedSubmissionId, orderHash); } catch (PartnerMetadataException e) { logger.logError( "Unable to save metadata for receivedSubmissionId " + receivedSubmissionId, e); diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java index ac3e95fb9..3fe338403 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/localfile/MockRSEndpointClient.java @@ -44,6 +44,8 @@ public String requestWatersEndpoint(String body, String bearerToken) public String requestHistoryEndpoint(String submissionId, String bearerToken) { return """ { + "timestamp" : "2020-01-01T00:00:00.000Z", + "sender" : "flexion.simulated-hospital", "destinations": [{ "organization_id": "flexion", "service": "simulated-lab" diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataExceptionTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataExceptionTest.groovy index a2a617b42..a3988f02d 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataExceptionTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataExceptionTest.groovy @@ -4,8 +4,7 @@ package gov.hhs.cdc.trustedintermediary.etor.metadata import spock.lang.Specification class PartnerMetadataExceptionTest extends Specification { - def "constructor works"() { - + def "two param constructor works"() { given: def message = "something blew up!" def cause = new NullPointerException() @@ -17,4 +16,15 @@ class PartnerMetadataExceptionTest extends Specification { exception.getMessage() == message exception.getCause() == cause } + + def "single param constructor works"() { + given: + def message = "something blew up!" + + when: + def exception = new PartnerMetadataException(message) + + then: + exception.getMessage() == message + } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy index 95105eb7b..d70a8bcd4 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/metadata/PartnerMetadataOrchestratorTest.groovy @@ -19,6 +19,100 @@ class PartnerMetadataOrchestratorTest extends Specification { TestApplicationContext.register(PartnerMetadataOrchestrator, PartnerMetadataOrchestrator.getInstance()) } + def "updateMetadataForReceivedOrder updates metadata successfully"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def sender = "senderName" + def timestamp = "2020-01-01T00:00:00.000Z" + def hashCode = "123" + def bearerToken = "token" + def rsHistoryApiResponse = "{\"sender\": \"${sender}\", \"timestamp\": \"${timestamp}\"}" + + def partnerMetadataStorage = Mock(PartnerMetadataStorage) + TestApplicationContext.register(PartnerMetadataStorage, partnerMetadataStorage) + + def mockClient = Mock(RSEndpointClient) + TestApplicationContext.register(RSEndpointClient, mockClient) + + def mockFormatter = Mock(Formatter) + mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [sender: sender, timestamp: timestamp] + TestApplicationContext.register(Formatter, mockFormatter) + + TestApplicationContext.injectRegisteredImplementations() + + when: + PartnerMetadataOrchestrator.getInstance().updateMetadataForReceivedOrder(receivedSubmissionId, hashCode) + + then: + 1 * mockClient.getRsToken() >> bearerToken + 1 * mockClient.requestHistoryEndpoint(receivedSubmissionId, bearerToken) >> rsHistoryApiResponse + 1 * partnerMetadataStorage.saveMetadata(new PartnerMetadata(receivedSubmissionId, sender, Instant.parse(timestamp), hashCode)) + } + + def "updateMetadataForReceivedOrder throws PartnerMetadataException on client error"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + + def mockClient = Mock(RSEndpointClient) + mockClient.getRsToken() >> "token" + mockClient.requestHistoryEndpoint(_ as String, _ as String) >> { throw new ReportStreamEndpointClientException("Client error", new Exception()) } + TestApplicationContext.register(RSEndpointClient, mockClient) + + TestApplicationContext.injectRegisteredImplementations() + + when: + PartnerMetadataOrchestrator.getInstance().updateMetadataForReceivedOrder(receivedSubmissionId, "hash") + + then: + thrown(PartnerMetadataException) + } + + def "updateMetadataForReceivedOrder throws PartnerMetadataException on formatter error"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def rsHistoryApiResponse = "{\"sender\": \"responseName\", \"timestamp\": \"2020-01-01T00:00:00.000Z\"}" + + def mockClient = Mock(RSEndpointClient) + mockClient.getRsToken() >> "token" + mockClient.requestHistoryEndpoint(_ as String, _ as String) >> rsHistoryApiResponse + TestApplicationContext.register(RSEndpointClient, mockClient) + + def mockFormatter = Mock(Formatter) + mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> { throw new FormatterProcessingException("Formatter error", new Exception()) } + TestApplicationContext.register(Formatter, mockFormatter) + + TestApplicationContext.injectRegisteredImplementations() + + when: + PartnerMetadataOrchestrator.getInstance().updateMetadataForReceivedOrder(receivedSubmissionId, "hash") + + then: + thrown(PartnerMetadataException) + } + + def "updateMetadataForReceivedOrder throws PartnerMetadataException on formatter error due to unexpected response format"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def wrongFormatResponse = "{\"someotherkey\": \"value\"}" + + def mockClient = Mock(RSEndpointClient) + mockClient.getRsToken() >> "token" + mockClient.requestHistoryEndpoint(_ as String, _ as String) >> wrongFormatResponse + TestApplicationContext.register(RSEndpointClient, mockClient) + + def mockFormatter = Mock(Formatter) + mockFormatter.convertJsonToObject(wrongFormatResponse, _ as TypeReference) >> [someotherkey: "value"] + TestApplicationContext.register(Formatter, mockFormatter) + + TestApplicationContext.injectRegisteredImplementations() + + when: + PartnerMetadataOrchestrator.getInstance().updateMetadataForReceivedOrder(receivedSubmissionId, "hash") + + then: + thrown(PartnerMetadataException) + } + def "updateMetadataForSentOrder updates metadata successfully"() { given: def receivedSubmissionId = "receivedSubmissionId" @@ -53,6 +147,22 @@ class PartnerMetadataOrchestratorTest extends Specification { 1 * partnerMetadataStorage.saveMetadata(updatedPartnerMetadata) } + def "updateMetadataForSentOrder test case when sentSubmissionId is null"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def sentSubmissionId = null + def partnerMetadataStorage = Mock(PartnerMetadataStorage) + + TestApplicationContext.register(PartnerMetadataStorage, partnerMetadataStorage) + TestApplicationContext.injectRegisteredImplementations() + + when: + PartnerMetadataOrchestrator.getInstance().updateMetadataForSentOrder(receivedSubmissionId, sentSubmissionId) + + then: + 0 * partnerMetadataStorage.readMetadata(receivedSubmissionId) + } + def "updateMetadataForSentOrder throws PartnerMetadataException on client error"() { given: def receivedSubmissionId = "receivedSubmissionId" @@ -125,6 +235,46 @@ class PartnerMetadataOrchestratorTest extends Specification { 1 * partnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(metadata) } + def "getMetadata gets receiver if missing from metadata"() { + given: + def receivedSubmissionId = "receivedSubmissionId" + def sentSubmissionId = "sentSubmissionId" + def sender = "senderName" + def timestamp = Instant.now() + def hashCode = "123" + def bearerToken = "token" + def rsHistoryApiResponse = "{\"destinations\": [{\"organization_id\": \"org\", \"service\": \"service\"}]}" + + PartnerMetadata missingReceiverMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, null, timestamp, hashCode) + PartnerMetadata expectedMetadata = new PartnerMetadata(receivedSubmissionId, sentSubmissionId, sender, "org.service", timestamp, hashCode) + + def partnerMetadataStorage = Mock(PartnerMetadataStorage) + TestApplicationContext.register(PartnerMetadataStorage, partnerMetadataStorage) + + def mockClient = Mock(RSEndpointClient) + mockClient.getRsToken() >> bearerToken + mockClient.requestHistoryEndpoint(sentSubmissionId, bearerToken) >> rsHistoryApiResponse + TestApplicationContext.register(RSEndpointClient, mockClient) + + def mockFormatter = Mock(Formatter) + mockFormatter.convertJsonToObject(rsHistoryApiResponse, _ as TypeReference) >> [destinations: [ + [organization_id: "org", service: "service"] + ]] + TestApplicationContext.register(Formatter, mockFormatter) + + TestApplicationContext.injectRegisteredImplementations() + + when: + Optional result = PartnerMetadataOrchestrator.getInstance().getMetadata(receivedSubmissionId) + + then: + result.isPresent() + result.get() == expectedMetadata + 2 * partnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(missingReceiverMetadata) + 1 * partnerMetadataStorage.saveMetadata(expectedMetadata) + 1 * partnerMetadataStorage.readMetadata(receivedSubmissionId) >> Optional.of(expectedMetadata) + } + def "getReceiverName returns correct receiver name from valid JSON response"() { given: String validJson = "{\"destinations\": [{\"organization_id\": \"org_id\", \"service\": \"service_name\"}]}" @@ -139,70 +289,48 @@ class PartnerMetadataOrchestratorTest extends Specification { receiverName == "org_id.service_name" } - def "getReceiverName throws FormatterProcessingException for invalid JSON response"() { + def "getReceiverName throws FormatterProcessingException for unexpected format response"() { given: - String invalidJson = "invalid JSON" - TestApplicationContext.register(Formatter, Jackson.getInstance()) TestApplicationContext.injectRegisteredImplementations() when: + String invalidJson = "invalid JSON" PartnerMetadataOrchestrator.getInstance().getReceiverName(invalidJson) then: thrown(FormatterProcessingException) - } - def "getReceiverName throws FormatterProcessingException for JSON without destinations"() { - given: - String jsonWithoutDestinations = "{\"someotherkey\": \"value\"}" + when: + String emptyJson = "{}" + PartnerMetadataOrchestrator.getInstance().getReceiverName(emptyJson) - TestApplicationContext.register(Formatter, Jackson.getInstance()) - TestApplicationContext.injectRegisteredImplementations() + then: + thrown(FormatterProcessingException) when: + String jsonWithoutDestinations = "{\"someotherkey\": \"value\"}" PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutDestinations) then: thrown(FormatterProcessingException) - } - - def "getReceiverName throws FormatterProcessingException for JSON with empty destinations"() { - given: - String jsonWithEmptyDestinations = "{\"destinations\": []}" - - TestApplicationContext.register(Formatter, Jackson.getInstance()) - TestApplicationContext.injectRegisteredImplementations() when: + String jsonWithEmptyDestinations = "{\"destinations\": []}" PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithEmptyDestinations) then: thrown(FormatterProcessingException) - } - - def "getReceiverName throws FormatterProcessingException for JSON without organization_id"() { - given: - String jsonWithoutOrgId = "{\"destinations\":[{\"service\":\"service\"}]}" - - TestApplicationContext.register(Formatter, Jackson.getInstance()) - TestApplicationContext.injectRegisteredImplementations() when: + String jsonWithoutOrgId = "{\"destinations\":[{\"service\":\"service\"}]}" PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutOrgId) then: thrown(FormatterProcessingException) - } - - def "getReceiverName throws FormatterProcessingException for JSON without service"() { - given: - String jsonWithoutService = "{\"destinations\":[{\"organization_id\":\"org_id\"}]}" - - TestApplicationContext.register(Formatter, Jackson.getInstance()) - TestApplicationContext.injectRegisteredImplementations() when: + String jsonWithoutService = "{\"destinations\":[{\"organization_id\":\"org_id\"}]}" PartnerMetadataOrchestrator.getInstance().getReceiverName(jsonWithoutService) then: