Skip to content

Commit 421e5b1

Browse files
committed
Release 0.5.7
- Move CompositeGraphResponse adapter - Minor corrections to Request and Response Info - Add MilestoneBillingE2ETest - Minor doc edits Signed-off-by: Gopal S Akshintala <[email protected]>
1 parent 4fa2026 commit 421e5b1

File tree

24 files changed

+3582
-65
lines changed

24 files changed

+3582
-65
lines changed

README.adoc

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ endif::[]
1818
:pmtemplates: src/integrationTest/resources/pm-templates
1919
:imagesdir: docs/images
2020
:prewrap!:
21-
:revoman-version: 0.5.6
21+
:revoman-version: 0.5.7
2222

2323
'''
2424

@@ -209,7 +209,7 @@ StepReport(
209209
envSnapshot: PostmanEnvironment<Any?> // <2>
210210
)
211211
----
212-
<1> https://docs.vavr.io/#_either[`Either` type from the VAVR] library represents either of the two states, error or success
212+
<1> https://docs.vavr.io/#_either[`Either` type from the VAVR] library represents either of the two states, used here to represent error or success
213213
<2> Snapshot of Environment at the end of each step execution. It can be compared with previous or next step environment snapshots to see what changed in this step
214214

215215
[.lead]
@@ -399,8 +399,8 @@ where you can filter out these legacy types from Marshalling/Unmarshalling
399399
* You may use the bundled static factory methods named `RequestConfig.unmarshallResponse()` for expressiveness.
400400
* You can pass a `PreTxnStepPick` which is a `Predicate` used
401401
to qualify a step whose Request JSON payload needs to be unmarshalled/deserialized.
402-
* If you have set up `requestConfig()` once, wherever you wish to access the request in your <<#_pre_step_and_post_step_hooks,Pre-Step Hooks>>, you can call `stepReport.responseInfo.get().<TypeT>getTypedTxnObj()` which returns a Strong type to conveniently assert.
403-
* If you don't pass anything in this config, Moshi unmarshalls into default data structures like `LinkedHashMap`
402+
* If you have set up `requestConfig()` once, wherever you wish to read the request in your <<#_pre_step_and_post_step_hooks,Pre-Step Hooks>>, you can call `stepReport.requestInfo.get().<TypeT>getTypedTxnObj()` which returns your request JSON as a Strong type.
403+
* If you don't configure it for a step, Moshi unmarshalls the step request JSON into default data structures like `LinkedHashMap`
404404

405405
==== `responseConfig()`
406406

@@ -409,8 +409,8 @@ to qualify a step whose Request JSON payload needs to be unmarshalled/deserializ
409409
* Use bundled static factory methods like `ResponseConfig.unmarshallSuccessResponse()` and `ResponseConfig.unmarshallErrorResponse()` for expressiveness.
410410
* You can pass a `PostTxnStepPick` which is a `Predicate` used
411411
to qualify a step whose Response JSON payload needs to be unmarshalled/deserialized.
412-
* If you have set up `responseConfig()` once, wherever you wish to access or assert the response in your <<#_pre_step_and_post_step_hooks,Post-Step Hooks>>, you can call `stepReport.responseInfo.get().<TypeT>getTypedTxnObj()` which returns a Strong type to conveniently assert.
413-
* If you don't pass anything in this config, Moshi unmarshalls into default data structures like `LinkedHashMap`
412+
* If you have set up `responseConfig()` once, wherever you wish to read or assert the response in your <<#_pre_step_and_post_step_hooks,Post-Step Hooks>>, you can call `stepReport.responseInfo.get().<TypeT>getTypedTxnObj()` which returns your response JSON as a Strong type to conveniently assert.
413+
* If you don't configure it for a step, Moshi unmarshalls the Step response JSON into default data structures like `LinkedHashMap`
414414

415415
[TIP]
416416
====
@@ -425,8 +425,8 @@ See `DiMorphicAdapter` in action from the test `compositeGraphResponseDiMorphicM
425425

426426
==== `globalCustomTypeAdapters()`
427427

428-
These adapters are used for marshalling/unmarshalling request/response payload for any step.
429-
These compliment the custom adapters setup in `requestConfig()` or `responseConfig()`
428+
* These come handy when the same POJO/Data structure (e.g `ID`) is present across multiple Request or Response POJOs. These adapters compliment the custom adapters setup in `requestConfig()` or `responseConfig()` wherever these types are present.
429+
* But these adapters won't be used to marshall/unmarshall before or after each step execution. Which means you won't be able to Strong types in the debug view. Although, you can get them on-demand using the respective `getTypedObj()` method.
430430

431431
==== JSON Reader/Writer Utils to build Moshi adapters
432432

@@ -448,12 +448,6 @@ See in Action:
448448
* link:{integrationtestdir}/com/salesforce/revoman/input/json/JsonPojoUtils2Test.java[JsonPojoUtils2Test]
449449
====
450450

451-
==== `rundown.mutableEnv.getTypedObj()`
452-
453-
You can read an environment variable value as a Strong type.
454-
See it in action: `getTypedObj()`
455-
test from link:{testdir}/com/salesforce/revoman/output/postman/PostmanEnvironmentTest.java[PostmanEnvironmentTest]
456-
457451
=== Execution Control
458452

459453
The configuration offers methods through which the execution strategy can be controlled without making any changes to the template:
@@ -577,6 +571,12 @@ so you can copy and import that environment conveniently into Postman.
577571
TIP: You do NOT need to save the copied Postman environment JSON from the debugger into file.
578572
You can paste (kbd:[Ctrl+v]) directly in the Postman environment window
579573

574+
==== Read Environment value into Strong type
575+
576+
You can read any value from mutableEnv as a Strong type using `rundown.mutableEnv.<TypeT>getTypedObj()`
577+
See it in action: `getTypedObj()`
578+
test from link:{testdir}/com/salesforce/revoman/output/postman/PostmanEnvironmentTest.java[PostmanEnvironmentTest]
579+
580580
==== `pmEnvSnapshot` in each StepReport
581581

582582
Each StepReport also has a `pmEnvSnapshot` to assert if a step has executed as expected and compare snapshots from different steps to examine the execution progress.
@@ -585,7 +585,7 @@ Each StepReport also has a `pmEnvSnapshot` to assert if a step has executed as e
585585

586586
* You don't have to squash all your steps into one mega collection. Instead, you can break them into easy-to-manage modular collections. `ReVoman.revUp()` accepts a list of collection paths through `templatePaths()`
587587
* But that doesn't mean you have to execute all these templates in one-go. You can make multiple `ReVoman.revUp()` calls for different collections.
588-
* If you wish to compose these executions in a specific order, you can use the `revUp` overload which accepts a vararg `Kick` configs.
588+
* If you wish to compose these executions in a specific order, you can use the `revUp()` overload which accepts a vararg `Kick` configs.
589589
** You can also achieve the same yourself, by adding the previous execution's `mutableEnv` to the current execution using the `dynamicEnvironment` parameter. This also comes in handy when you wish to execute a common step (e.g. `UserSetup`) inside a Test setup method and use that environment for all the tests.
590590

591591
=== Custom Dynamic variables
@@ -603,6 +603,12 @@ TIP: Here is an example of a low-code link:{integrationtestdir}/com/salesforce/r
603603
Compared to a traditional Integration/Functional or E2E test, approximately, the amount of code needed is *89%* less using ReṼoman.
604604
The above test doesn't just have low code, but also low in Cognitive Complexity and Transparent in what it does.
605605

606+
=== Low Learning Curve
607+
608+
Familiarity with Postman gets you a long way in understanding this tool.
609+
Playing with the existing link:{integrationtestdir}[Integration Tests]
610+
and writing a couple of hands-on tests should get you started.
611+
606612
=== CI/CD integrability
607613

608614
* ReṼoman is like any JVM library that you can plug into any JVM program/test (e.g., JUnit tests or Integration tests).

buildSrc/src/main/kotlin/Config.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
* ************************************************************************************************
77
*/
88
const val GROUP_ID = "com.salesforce.revoman"
9-
const val VERSION = "0.5.6"
9+
const val VERSION = "0.5.7"
1010
const val ARTIFACT_ID = "revoman"
1111
const val STAGING_PROFILE_ID = "1ea0a23e61ba7d"

src/integrationTest/java/com/salesforce/revoman/input/json/JsonPojoUtils2Test.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
import static com.google.common.truth.Truth.assertThat;
1111
import static com.salesforce.revoman.input.FileUtils.readFileInResourcesToString;
12-
import static com.salesforce.revoman.integration.core.pq.adapters.ConnectInputRepWithGraphAdapter.adapter;
12+
import static com.salesforce.revoman.integration.core.adapters.ConnectInputRepWithGraphAdapter.adapter;
1313
import static org.junit.jupiter.api.Assertions.assertNotNull;
1414
import static org.junit.jupiter.api.Assertions.assertThrows;
1515

16-
import com.salesforce.revoman.integration.core.pq.adapters.IDAdapter;
16+
import com.salesforce.revoman.integration.core.adapters.IDAdapter;
1717
import com.salesforce.revoman.integration.core.pq.connect.request.PlaceQuoteInputRepresentation;
1818
import com.salesforce.revoman.integration.core.pq.connect.request.PricingPreferenceEnum;
1919
import com.salesforce.revoman.integration.core.pq.connect.response.PlaceQuoteOutputRepresentation;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/***************************************************************************************************
2+
* Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier:
3+
* Apache License Version 2.0
4+
* For full license text, see the LICENSE file in the repo root or
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
**************************************************************************************************/
7+
8+
package com.salesforce.revoman.integration.core;
9+
10+
import static com.salesforce.revoman.input.config.HookConfig.post;
11+
import static com.salesforce.revoman.input.config.ResponseConfig.unmarshallResponse;
12+
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepContainingURIPathOfAny;
13+
import static org.junit.jupiter.api.Assertions.assertTrue;
14+
15+
import com.salesforce.revoman.input.config.HookConfig;
16+
import com.salesforce.revoman.input.config.ResponseConfig;
17+
import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse;
18+
import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse.Graph.ErrorGraph;
19+
import com.salesforce.revoman.output.report.StepReport;
20+
import kotlin.collections.CollectionsKt;
21+
22+
public class CoreUtils {
23+
private CoreUtils() {}
24+
25+
public static final String COMPOSITE_GRAPH_URI_PATH = "composite/graph";
26+
27+
public static final HookConfig ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS =
28+
post(
29+
afterStepContainingURIPathOfAny(COMPOSITE_GRAPH_URI_PATH),
30+
(stepReport, ignore) -> assertCompositeGraphResponseSuccess(stepReport));
31+
32+
public static ResponseConfig unmarshallCompositeGraphResponse() {
33+
return unmarshallResponse(
34+
afterStepContainingURIPathOfAny(COMPOSITE_GRAPH_URI_PATH),
35+
CompositeGraphResponse.class,
36+
CompositeGraphResponse.ADAPTER);
37+
}
38+
39+
public static void assertCompositeGraphResponseSuccess(StepReport stepReport) {
40+
final var responseTxnInfo = stepReport.responseInfo.get();
41+
final var graphResp =
42+
responseTxnInfo.<CompositeGraphResponse>getTypedTxnObj().getGraphs().get(0);
43+
assertTrue(
44+
graphResp.isSuccessful(),
45+
() -> {
46+
final var firstErrorResponse =
47+
CollectionsKt.first(((ErrorGraph) graphResp).errorResponses);
48+
final var firstErrorResponseBody = ((ErrorGraph) graphResp).firstErrorResponseBody;
49+
return String.format(
50+
"Unsuccessful Composite Graph response%n{%n first error ReferenceId: %s%n first errorCode: %s%n first errorMessage: %s%n}%n%s",
51+
firstErrorResponse.getReferenceId(),
52+
firstErrorResponseBody.getErrorCode(),
53+
firstErrorResponseBody.getMessage(),
54+
stepReport);
55+
});
56+
}
57+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* http://www.apache.org/licenses/LICENSE-2.0
66
**************************************************************************************************/
77

8-
package com.salesforce.revoman.integration.core.pq.adapters;
8+
package com.salesforce.revoman.integration.core.adapters;
99

1010
import static com.salesforce.revoman.input.json.JsonReaderUtils.anyMapR;
1111
import static com.salesforce.revoman.input.json.JsonReaderUtils.listR;
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
* http://www.apache.org/licenses/LICENSE-2.0
66
**************************************************************************************************/
77

8-
package com.salesforce.revoman.integration.core.pq.adapters;
8+
package com.salesforce.revoman.integration.core.adapters;
99

1010
import com.salesforce.revoman.integration.core.pq.connect.response.ID;
1111
import com.squareup.moshi.FromJson;
1212
import com.squareup.moshi.ToJson;
1313

1414
// * NOTE 10 Mar 2024 gopala.akshintala: Custom Type Adapter
1515
public class IDAdapter {
16+
public static final IDAdapter INSTANCE = new IDAdapter();
17+
1618
@FromJson
1719
ID fromJson(String id) {
1820
return new ID(id);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2025 salesforce.com, inc.
3+
* All Rights Reserved
4+
* Company Confidential
5+
*/
6+
7+
package com.salesforce.revoman.integration.core.bt2bs;
8+
9+
import static com.google.common.truth.Truth.assertThat;
10+
import static com.salesforce.revoman.integration.core.bt2bs.ReVomanConfigForBT2BS.MILESTONE_CONFIG;
11+
import static com.salesforce.revoman.integration.core.bt2bs.ReVomanConfigForBT2BS.MILESTONE_SETUP_CONFIG;
12+
import static com.salesforce.revoman.integration.core.bt2bs.ReVomanConfigForBT2BS.USER_CREATION_AND_SETUP_CONFIG;
13+
14+
import com.salesforce.revoman.ReVoman;
15+
import java.util.Map;
16+
import kotlin.collections.CollectionsKt;
17+
import org.junit.jupiter.api.Test;
18+
19+
class MilestoneBillingE2ETest {
20+
21+
@Test
22+
void testMilestoneBillingE2E() {
23+
final var mbRundown =
24+
ReVoman.revUp(
25+
(rundown, ignore) ->
26+
assertThat(rundown.firstUnIgnoredUnsuccessfulStepReport()).isNull(),
27+
USER_CREATION_AND_SETUP_CONFIG,
28+
MILESTONE_SETUP_CONFIG,
29+
MILESTONE_CONFIG);
30+
assertThat(CollectionsKt.last(mbRundown).mutableEnv)
31+
.containsAtLeastEntriesIn(
32+
Map.of(
33+
"billingMilestonePlan1Status", "Completely Billed",
34+
"billingMilestonePlanItem1Status", "Invoiced",
35+
"billingSchedule1Status", "CompletelyBilled",
36+
"invoice1Status", "Posted",
37+
"invoice2Status", "Posted"));
38+
}
39+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2025 salesforce.com, inc.
3+
* All Rights Reserved
4+
* Company Confidential
5+
*/
6+
7+
package com.salesforce.revoman.integration.core.bt2bs;
8+
9+
import static com.salesforce.revoman.input.config.HookConfig.post;
10+
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepContainingHeader;
11+
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepContainingURIPathOfAny;
12+
import static com.salesforce.revoman.integration.core.CoreUtils.ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS;
13+
import static com.salesforce.revoman.integration.core.CoreUtils.unmarshallCompositeGraphResponse;
14+
import static com.salesforce.revoman.output.ExeType.HTTP_STATUS;
15+
16+
import com.salesforce.revoman.input.config.HookConfig;
17+
import com.salesforce.revoman.input.config.Kick;
18+
import com.salesforce.revoman.integration.core.adapters.IDAdapter;
19+
20+
public final class ReVomanConfigForBT2BS {
21+
22+
private ReVomanConfigForBT2BS() {}
23+
24+
static final String ENV_PATH = "pm-templates/core/milestone/env.postman_environment.json";
25+
static final String IGNORE_HTTP_STATUS_UNSUCCESSFUL = "ignoreHTTPStatusUnsuccessful";
26+
static final String NODE_MODULE_RELATIVE_PATH = "js";
27+
28+
// ## User creation and Setup
29+
static final String COLLECTION_PATH = "pm-templates/core/milestone/";
30+
private static final String USER_CREATION_AND_SETUP_COLLECTION_PATH =
31+
COLLECTION_PATH + "user-creation-and-setup.postman_collection.json";
32+
static final Kick USER_CREATION_AND_SETUP_CONFIG =
33+
Kick.configure()
34+
.templatePath(USER_CREATION_AND_SETUP_COLLECTION_PATH)
35+
.environmentPath(ENV_PATH)
36+
.responseConfig(unmarshallCompositeGraphResponse())
37+
.hook(ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS)
38+
.nodeModulesRelativePath(NODE_MODULE_RELATIVE_PATH)
39+
.haltOnFailureOfTypeExcept(
40+
HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
41+
.insecureHttp(true)
42+
.off();
43+
44+
// ## Hooks
45+
static final String PST = "connect/rev/sales-transaction/actions/place";
46+
static final String STANDALONE_INVOICE_IA =
47+
"commerce/invoicing/invoices/collection/actions/generate";
48+
static final String ASSETIZE_IA = "actions/standard/createOrUpdateAssetFromOrder";
49+
static final String AMEND_API = "connect/revenue-management/assets/actions/amend";
50+
static final String CANCEL_API = "connect/revenue-management/assets/actions/cancel";
51+
public static final HookConfig MEMQ_AWAIT =
52+
post(
53+
afterStepContainingURIPathOfAny(
54+
PST, STANDALONE_INVOICE_IA, ASSETIZE_IA, AMEND_API, CANCEL_API),
55+
(ignore1, ignore2) -> Thread.sleep(5000));
56+
57+
// ## Milestone Setup Config
58+
private static final String MB_SETUP_POSTMAN_COLLECTION_PATH =
59+
COLLECTION_PATH + "milestone-setup.postman_collection.json";
60+
static final Kick MILESTONE_SETUP_CONFIG =
61+
Kick.configure()
62+
.templatePath(MB_SETUP_POSTMAN_COLLECTION_PATH)
63+
.haltOnFailureOfTypeExcept(
64+
HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
65+
.responseConfig(unmarshallCompositeGraphResponse())
66+
.hook(ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS)
67+
.nodeModulesRelativePath(NODE_MODULE_RELATIVE_PATH)
68+
.insecureHttp(true)
69+
.off();
70+
71+
// ## Milestone Config
72+
private static final String MB_POSTMAN_COLLECTION_PATH =
73+
COLLECTION_PATH + "bmp-create-runtime.postman_collection.json";
74+
static final Kick MILESTONE_CONFIG =
75+
Kick.configure()
76+
.templatePath(MB_POSTMAN_COLLECTION_PATH)
77+
.responseConfig(unmarshallCompositeGraphResponse())
78+
.hooks(MEMQ_AWAIT, ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS)
79+
.globalCustomTypeAdapter(IDAdapter.INSTANCE)
80+
.nodeModulesRelativePath(NODE_MODULE_RELATIVE_PATH)
81+
.insecureHttp(true)
82+
.off();
83+
}

0 commit comments

Comments
 (0)