├── .asciidoctor
└── docinfo.html
├── .asciidoctorconfig
├── .editorconfig
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .idea
├── GitLink.xml
├── cody_history.xml
├── copyright
│ ├── oss_sf.xml
│ └── profiles_settings.xml
├── detekt.xml
├── git_toolbox_blame.xml
├── git_toolbox_prj.xml
├── icon.png
├── kotlin-statistics.xml
├── kotlinc.xml
├── ktfmt.xml
├── misc.xml
└── vcs.xml
├── BUILD.bazel
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.adoc
├── LICENSE
├── MODULE.bazel
├── MODULE.bazel.lock
├── README.adoc
├── SECURITY.md
├── WORKSPACE
├── build.gradle.kts
├── buildSrc
├── build.gradle.kts
├── settings.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── Config.kt
│ ├── preDef.kt
│ ├── revoman.kt-conventions.gradle.kts
│ ├── revoman.publishing-conventions.gradle.kts
│ └── revoman.root-conventions.gradle.kts
├── detekt
├── baseline.xml
└── config.yml
├── docs
├── images
│ ├── cognitive-complexity.png
│ ├── failure-hierarchy.png
│ ├── hybrid-tool.png
│ ├── manual-to-automation.png
│ ├── mutable-env.png
│ ├── node_modules.png
│ ├── postman-run.png
│ ├── pq-exe-logging.gif
│ ├── pq-revoman-test-time.png
│ ├── resfulapi-dev-pm.png
│ ├── revoman-demo-thumbnail.png
│ ├── rundown.png
│ ├── step-procedure.png
│ └── step-report.png
├── release-notes
│ └── revoman-2.0.adoc
└── revoman.exe.log
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── js
├── package-lock.json
└── package.json
├── libs.versions.toml
├── qodana.yaml
├── renovate.json
├── settings.gradle.kts
└── src
├── integrationTest
├── java
│ └── com
│ │ └── salesforce
│ │ └── revoman
│ │ ├── input
│ │ └── json
│ │ │ └── JsonPojoUtils2Test.java
│ │ └── integration
│ │ ├── core
│ │ ├── CoreUtils.java
│ │ ├── adapters
│ │ │ ├── ConnectInputRepWithGraphAdapter.java
│ │ │ └── IDAdapter.java
│ │ ├── bt2bs
│ │ │ ├── MilestoneBillingE2ETest.java
│ │ │ └── ReVomanConfigForBT2BS.java
│ │ └── pq
│ │ │ ├── PQE2EWithSMTest.java
│ │ │ └── connect
│ │ │ ├── request
│ │ │ ├── ConnectInputRepresentationWithGraph.java
│ │ │ ├── ObjectGraphInputRepresentation.java
│ │ │ ├── ObjectInputRepresentationMap.java
│ │ │ ├── ObjectWithReferenceInputRepresentation.java
│ │ │ ├── ObjectWithReferenceInputRepresentationList.java
│ │ │ ├── PlaceQuoteInputRepresentation.java
│ │ │ └── PricingPreferenceEnum.java
│ │ │ └── response
│ │ │ ├── ID.java
│ │ │ ├── PlaceQuoteErrorResponseRepresentation.java
│ │ │ └── PlaceQuoteOutputRepresentation.java
│ │ ├── pokemon
│ │ └── PokemonTest.java
│ │ └── restfulapidev
│ │ └── RestfulAPIDevTest.java
├── kotlin
│ └── com
│ │ └── salesforce
│ │ └── revoman
│ │ └── integration
│ │ ├── apigee
│ │ └── ApigeeKtTest.kt
│ │ └── restfulapidev
│ │ └── RestfulAPIDevKtTest.kt
└── resources
│ ├── json
│ ├── pq-payload.json
│ ├── pq-response.json
│ └── pricing-pref.json
│ ├── log4j2.xml
│ └── pm-templates
│ ├── apigee
│ └── apigee.postman_collection.json
│ ├── core
│ ├── milestone
│ │ ├── bmp-create-runtime.postman_collection.json
│ │ ├── env.postman_environment.json
│ │ ├── milestone-setup.postman_collection.json
│ │ └── persona-creation-and-setup.postman_collection.json
│ └── pq
│ │ ├── [sm] pq.postman_collection.json
│ │ ├── [sm] user-creation-with-ps-and-setup-pq.postman_collection.json
│ │ ├── pq-env.postman_environment.json
│ │ └── pre-salesRep.postman_collection.json
│ ├── pokemon
│ ├── pokemon.postman_collection.json
│ └── pokemon.postman_environment.json
│ └── restfulapidev
│ ├── postman-cli-reports
│ └── restful-api.dev-2025-04-14-13-16-43.json
│ ├── restful-api.dev.postman_collection.json
│ └── restful-api.dev.postman_environment.json
├── main
├── kotlin
│ └── com
│ │ └── salesforce
│ │ └── revoman
│ │ ├── ReVoman.kt
│ │ ├── input
│ │ ├── FileUtils.kt
│ │ ├── PostExeHook.kt
│ │ ├── config
│ │ │ ├── HookConfig.kt
│ │ │ ├── KickDef.kt
│ │ │ ├── RequestConfig.kt
│ │ │ ├── ResponseConfig.kt
│ │ │ └── StepPick.kt
│ │ └── json
│ │ │ ├── FnTypes.kt
│ │ │ ├── JsonPojoUtils.kt
│ │ │ ├── JsonReaderUtils.kt
│ │ │ ├── JsonWriterUtils.kt
│ │ │ ├── adapters
│ │ │ └── salesforce
│ │ │ │ ├── CompositeGraphRequest.kt
│ │ │ │ ├── CompositeGraphResponse.kt
│ │ │ │ ├── CompositeResponse.kt
│ │ │ │ └── Constants.kt
│ │ │ └── factories
│ │ │ └── DiMorphicAdapter.kt
│ │ ├── internal
│ │ ├── exe
│ │ │ ├── ExeUtils.kt
│ │ │ ├── HttpRequest.kt
│ │ │ ├── PmJsEval.kt
│ │ │ ├── PostStepHook.kt
│ │ │ ├── PreStepHook.kt
│ │ │ ├── UnmarshallRequest.kt
│ │ │ └── UnmarshallResponse.kt
│ │ ├── json
│ │ │ ├── MoshiReVoman.kt
│ │ │ ├── adapters
│ │ │ │ ├── BigDecimalAdapter.kt
│ │ │ │ ├── EpochAdapter.kt
│ │ │ │ ├── TypeAdapter.kt
│ │ │ │ └── UUIDAdapter.kt
│ │ │ └── factories
│ │ │ │ ├── AlwaysSerializeNullsFactory.kt
│ │ │ │ ├── CaseInsensitiveEnumAdapter.kt
│ │ │ │ └── IgnoreTypesFactory.kt
│ │ └── postman
│ │ │ ├── DynamicVariableGenerator.kt
│ │ │ ├── PostmanSDK.kt
│ │ │ ├── RegexReplacer.kt
│ │ │ └── template
│ │ │ ├── Auth.kt
│ │ │ ├── Environment.kt
│ │ │ └── Template.kt
│ │ └── output
│ │ ├── ExeType.kt
│ │ ├── Rundown.kt
│ │ ├── postman
│ │ └── PostmanEnvironment.kt
│ │ └── report
│ │ ├── Step.kt
│ │ ├── StepReport.kt
│ │ ├── TxnInfo.kt
│ │ └── failure
│ │ ├── ExeFailure.kt
│ │ ├── HookFailure.kt
│ │ ├── HttpStatusUnsuccessful.kt
│ │ ├── RequestFailure.kt
│ │ └── ResponseFailure.kt
└── resources
│ └── log4j2.xml
└── test
├── java
└── com
│ └── salesforce
│ └── revoman
│ ├── input
│ └── json
│ │ ├── JsonPojoUtilsTest.java
│ │ ├── adapters
│ │ └── SObjectGraphRequestMarshaller.java
│ │ └── pojo
│ │ └── SObjectGraphRequest.java
│ └── output
│ └── postman
│ └── PostmanEnvironmentTest.java
├── kotlin
└── com
│ └── salesforce
│ └── revoman
│ ├── input
│ ├── EvalJsTest.kt
│ ├── FileUtilsTest.kt
│ └── config
│ │ └── KickTest.kt
│ ├── internal
│ ├── ExeUtilsTest.kt
│ └── postman
│ │ └── RegexReplacerTest.kt
│ └── output
│ └── report
│ ├── PostmanEnvironmentKtTest.kt
│ ├── StepReportTest.kt
│ └── TxnInfoTest.kt
└── resources
├── composite
├── graph
│ ├── req
│ │ └── graph-request.json
│ └── resp
│ │ ├── graph-response-error.json
│ │ └── graph-response-success.json
└── query
│ └── resp
│ ├── query-response-all-error.json
│ ├── query-response-all-success.json
│ └── query-response-partial-success.json
├── env-from-revoman.json
├── env-with-regex.json
├── json
├── nested-bean.json
└── pq-graph-req-masked.json
└── pm-templates
├── steps-with-folders.json
└── steps-without-folders.postman_collection.json
/.asciidoctor/docinfo.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
21 |
--------------------------------------------------------------------------------
/.asciidoctorconfig:
--------------------------------------------------------------------------------
1 | :experimental:
2 | :linkcss:
3 | :docinfodir: {asciidoctorconfigdir}/.asciidoctor
4 | :docinfo: shared
5 | :icons: font
6 | ifdef::env-github[]
7 | :tip-caption: :bulb:
8 | :note-caption: :information_source:
9 | :important-caption: :heavy_exclamation_mark:
10 | :caution-caption: :fire:
11 | :warning-caption: :warning:
12 | endif::[]
13 | :listing-caption: Snippet
14 | :sectnums:
15 | :source-linenums-option:
16 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: 'Build and Scan'
2 | on: push
3 | jobs:
4 | gradle:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v4
8 | - uses: actions/setup-java@v4
9 | with:
10 | distribution: jetbrains
11 | java-version: 17
12 |
13 | - name: 'Setup Gradle'
14 | uses: gradle/gradle-build-action@v3
15 |
16 | - name: 'Gradle test'
17 | run: ./gradlew test integrationTest --tests com.salesforce.revoman.integration.pokemon.PokemonTest
18 |
--------------------------------------------------------------------------------
/.idea/GitLink.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/cody_history.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/copyright/oss_sf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/detekt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/git_toolbox_blame.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/git_toolbox_prj.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/.idea/icon.png
--------------------------------------------------------------------------------
/.idea/kotlin-statistics.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/ktfmt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BUILD.bazel:
--------------------------------------------------------------------------------
1 | load("@rules_java//java:defs.bzl", "java_import")
2 |
3 | package(default_visibility = ["//visibility:public"])
4 |
5 | java_import(
6 | name = "com_salesforce_revoman_revoman",
7 | jars = glob(
8 | ["build/libs/revoman-root-*.jar"],
9 | allow_empty = False,
10 | ),
11 | srcjar = glob(
12 | ["build/libs/revoman-root-*-sources.jar"],
13 | allow_empty = False,
14 | )[0],
15 | )
16 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | #GUSINFO:Rev Delphinus,Rev Delphinus Trust
2 | *
3 | # Comment line immediately above ownership line is reserved for related other information. Please be careful while editing.
4 | #ECCN:Open Source 5D002
5 | @overfullstack
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.adoc:
--------------------------------------------------------------------------------
1 | = Contributing
2 | Gopal S Akshintala
3 | :Revision: 1.0
4 | ifdef::env-github[]
5 | :tip-caption: :bulb:
6 | :note-caption: :information_source:
7 | :important-caption: :heavy_exclamation_mark:
8 | :caution-caption: :fire:
9 | :warning-caption: :warning:
10 | endif::[]
11 | :icons: font
12 | ifdef::env-github[]
13 | :tip-caption: :bulb:
14 | :note-caption: :information_source:
15 | :important-caption: :heavy_exclamation_mark:
16 | :caution-caption: :fire:
17 | :warning-caption: :warning:
18 | endif::[]
19 | :hide-uri-scheme:
20 | :sourcedir: src/main/java
21 | :imagesdir: images
22 | :toc:
23 |
24 | == Source-code Setup
25 |
26 | === Install Java
27 |
28 | It needs JDK 17 installed in your system.
29 | Recommendation is to do it via https://sdkman.io/install[SDKMAN].
30 | After you install SDKMAN,
31 | run `sdk list java` -> Pick Identifier for your favorite java distribution -> Run `sdk install java `
32 | to install Java. For example:
33 |
34 | [source,bash]
35 | ----
36 | sdk install java 17.0.14-amzn
37 | ----
38 |
39 | === Build with Gradle
40 |
41 | This is a simple Gradle project and has its own Gradle wrapper. Nothing to install, run just this command:
42 |
43 | [source,bash]
44 | ----
45 | ./gradlew clean build
46 | ----
47 |
48 | TIP: You *don't* need a local Gradle installation as the `gradlew` (Gradle wrapper) takes care of everything. But if you wish to install Gradle locally, the recommendation is to do it via https://sdkman.io/install[SDKMAN]. After you install SDKMAN, run `sdk install gradle` to install Gradle
49 |
50 | You can run/debug the existing unit tests or write your own to play with the tool.
51 |
52 | === Kotlin
53 |
54 | * The code-base is a mix of Java and Kotlin.
55 | If you're a Java developer and new to Kotlin, don't worry, Kotlin is a JVM language and can be used anywhere Java is used.
56 | In fact, it has got the reputation of *"Better Java!"*.
57 | * A typical Java developer can ramp up on Kotlin in less than a week. These resources can help catalyze your ramp-up:
58 | ** https://play.kotlinlang.org/byExample/overview[Learn Kotlin by Example]
59 | ** https://www.coursera.org/learn/kotlin-for-java-developers[*Kotlin for Java Developers | Coursera*], a free course
60 | ** https://www.kotlinprimer.com/[The Kotlin Primer], tailor-made to facilitate Kotlin adoption inside Java-centric organizations
61 | * If you use Intellij, Kotlin plugin comes bundled.
62 | * If you use VS Code based IDEs (e.g. Cursor) install this official https://github.com/Kotlin/kotlin-lsp[Kotlin Language Server plugin]
63 | Similar development aids should be present for other code editors too.
64 |
65 | == Code Formatting
66 |
67 | This repo uses https://github.com/diffplug/spotless[*Spotless*] for formatting files. Please run `./gradlew spotlessApply` before check-in to fix any formatting errors.
68 |
69 | TIP: If you're on Intellij, replace your kbd:[Cmd+Shift+L] habit with kbd:[Ctrl]-kbd:[Ctrl]
70 | and run `./gradlew spotlessApply` (Or the respective action if you're on VS Code/Eclipse).
71 | It may be slow for the first run, but subsequent runs should be faster.
72 |
73 | == Manual publishing
74 |
75 | === Gradle Nexus Publish Plugin
76 |
77 | We use https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-central[Gradle Nexus Publish Plugin]
78 | to publish to Maven Central. Follow the usage instructions to set up Sonatype credentials and add them to your `~/.gradle/gradle.properties`.
79 |
80 | [source,bash]
81 | ----
82 | sonatypeUsername=
83 | sonatypePassword=
84 | ----
85 |
86 | === Versioning Strategy
87 |
88 | ====
89 | ..
90 | ====
91 |
92 | * A = Broke something on purpose (Breaking API change)
93 | * B = Profit (Feature / Improvement)
94 | * C = Broke something by accident (Bug)
95 |
96 | Follow the Versioning Strategy to increment version link:buildSrc/{sourcedir}/Config.kt[here].
97 | For SNAPSHOT releases, add a `-SNAPSHOT` at the end of version number
98 |
99 | === Publish
100 |
101 | Run this command to publish it to Nexus:
102 |
103 | [source,bash]
104 | ----
105 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -Dorg.gradle.parallel=false --no-configuration-cache
106 | ----
107 |
108 | * You can monitor for the new version jar to reflect in https://repo1.maven.org/maven2/com/salesforce/revoman/revoman/[Maven Central]. It usually takes less than 30 minutes.
109 |
110 | == Code of Conduct
111 | Please follow our link:CODE_OF_CONDUCT.md[Code of Conduct]
112 |
113 | == License
114 | By contributing your code,
115 | you agree to license your contribution under the terms of our project link:LICENSE[]
116 | and to sign the https://cla.salesforce.com/sign-cla[Salesforce CLA]
117 |
--------------------------------------------------------------------------------
/MODULE.bazel:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Bazel now uses Bzlmod by default to manage external dependencies.
3 | # Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel.
4 | #
5 | # For more details, please check https://github.com/bazelbuild/bazel/issues/18958
6 | ###############################################################################
7 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | ## Security
2 |
3 | Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com)
4 | as soon as it is discovered. This library limits its runtime dependencies in
5 | order to reduce the total cost of ownership as much as can be, but all consumers
6 | should remain vigilant and have their security stakeholders review all third-party
7 | products (3PP) like this one and their dependencies.
8 |
--------------------------------------------------------------------------------
/WORKSPACE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/WORKSPACE
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | plugins {
9 | id("revoman.root-conventions")
10 | id("revoman.publishing-conventions")
11 | id("revoman.kt-conventions")
12 | alias(libs.plugins.moshix)
13 | alias(libs.plugins.node.gradle)
14 | alias(libs.plugins.kover)
15 | alias(libs.plugins.nexus.publish)
16 | alias(libs.plugins.gradle.taskinfo)
17 | }
18 |
19 | val mockitoAgent = configurations.create("mockitoAgent")
20 |
21 | dependencies {
22 | api(platform(libs.http4k.bom))
23 | api(libs.bundles.http4k)
24 | api(libs.moshix.adapters)
25 | api(libs.java.vavr)
26 | api(libs.kotlin.vavr)
27 | api(libs.arrow.core)
28 | api(libs.kotlinx.datetime)
29 | implementation(libs.bundles.kotlin.logging)
30 | implementation(libs.pprint)
31 | implementation(libs.graal.js)
32 | implementation(libs.kotlin.faker)
33 | implementation(libs.underscore)
34 | implementation(libs.okio.jvm)
35 | implementation(libs.spring.beans)
36 | kapt(libs.immutables.value)
37 | compileOnly(libs.immutables.builder)
38 | compileOnly(libs.immutables.value.annotations)
39 | compileOnly(libs.jetbrains.annotations)
40 | testImplementation(libs.truth)
41 | testImplementation(libs.json.assert)
42 | mockitoAgent(libs.mockito.core) { isTransitive = false }
43 | testImplementation(libs.mockk)
44 | }
45 |
46 | testing {
47 | suites {
48 | val test by getting(JvmTestSuite::class) { useJUnitJupiter(libs.versions.junit.get()) }
49 |
50 | register("integrationTest") {
51 | dependencies {
52 | implementation(project())
53 | implementation(libs.truth)
54 | implementation(libs.mockito.core)
55 | implementation(libs.spring.beans)
56 | implementation(libs.json.assert)
57 | implementation(libs.assertj.vavr)
58 | }
59 | }
60 | }
61 | }
62 |
63 | node { nodeProjectDir = file("${project.projectDir}/js") }
64 |
65 | tasks {
66 | check { dependsOn(npmInstall) }
67 | test { dependsOn(npmInstall) }
68 | }
69 |
70 | kover { reports { total { html { onCheck = true } } } }
71 |
72 | moshi { enableSealed = true }
73 |
74 | nexusPublishing {
75 | this.repositories {
76 | sonatype {
77 | stagingProfileId = STAGING_PROFILE_ID
78 | nexusUrl = uri("https://ossrh-staging-api.central.sonatype.com/service/local/")
79 | snapshotRepositoryUrl = uri("https://central.sonatype.com/repository/maven-snapshots/")
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | plugins { `kotlin-dsl` }
9 |
10 | repositories {
11 | mavenCentral()
12 | gradlePluginPortal()
13 | maven("https://oss.sonatype.org/content/repositories/snapshots")
14 | }
15 |
16 | dependencies {
17 | implementation(libs.kotlin.gradle)
18 | implementation(libs.spotless.gradle)
19 | implementation(libs.detekt.gradle)
20 | implementation(libs.testLogger.gradle)
21 | }
22 |
--------------------------------------------------------------------------------
/buildSrc/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | dependencyResolutionManagement {
9 | versionCatalogs { create("libs") { from(files("../libs.versions.toml")) } }
10 |
11 | pluginManagement {
12 | repositories {
13 | mavenCentral()
14 | gradlePluginPortal()
15 | google()
16 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots")
17 | maven("https://oss.sonatype.org/content/repositories/snapshots")
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/Config.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | const val GROUP_ID = "com.salesforce.revoman"
9 | const val VERSION = "0.8.2"
10 | const val ARTIFACT_ID = "revoman"
11 | const val STAGING_PROFILE_ID = "1ea0a23e61ba7d"
12 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/preDef.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | import org.gradle.api.artifacts.ExternalModuleDependencyBundle
9 | import org.gradle.api.artifacts.VersionCatalog
10 | import org.gradle.api.artifacts.VersionConstraint
11 | import org.gradle.api.provider.Property
12 | import org.gradle.api.provider.Provider
13 | import org.gradle.plugin.use.PluginDependency
14 |
15 | val Provider.pluginId: String
16 | get() = get().pluginId
17 |
18 | infix fun Property.by(value: T) {
19 | set(value)
20 | }
21 |
22 | internal val VersionCatalog.jdk: VersionConstraint
23 | get() = getVersion("jdk")
24 |
25 | internal val VersionCatalog.kotestBundle: Provider
26 | get() = getBundle("kotest")
27 |
28 | private fun VersionCatalog.getLibrary(library: String) = findLibrary(library).get()
29 |
30 | private fun VersionCatalog.getBundle(bundle: String) = findBundle(bundle).get()
31 |
32 | private fun VersionCatalog.getPlugin(plugin: String) = findPlugin(plugin).get()
33 |
34 | private fun VersionCatalog.getVersion(plugin: String) = findVersion(plugin).get()
35 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/revoman.kt-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | plugins {
9 | kotlin("jvm")
10 | kotlin("kapt")
11 | }
12 |
13 | val libs: VersionCatalog = extensions.getByType().named("libs")
14 |
15 | dependencies { testImplementation(libs.kotestBundle) }
16 |
17 | kapt {
18 | useBuildCache = true
19 | }
20 |
21 | kotlin {
22 | jvmToolchain(libs.jdk.toString().toInt())
23 | compilerOptions {
24 | freeCompilerArgs.addAll("-Xjvm-default=all", "-Xcontext-receivers", "-Xconsistent-data-class-copy-visibility", "-Xmulti-dollar-interpolation")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/revoman.publishing-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 |
9 | plugins {
10 | `maven-publish`
11 | signing
12 | `java-library`
13 | }
14 |
15 | group = GROUP_ID
16 |
17 | version = VERSION
18 |
19 | description = "ReVoman - A template-driven API automation tool for JVM (Java/Kotlin)"
20 |
21 | repositories { mavenCentral() }
22 |
23 | java {
24 | withJavadocJar()
25 | withSourcesJar()
26 | }
27 |
28 | publishing {
29 | publications.create("revoman") {
30 | artifactId = ARTIFACT_ID
31 | from(components["java"])
32 | pom {
33 | name.set("revoman")
34 | description.set(project.description)
35 | url.set("https://github.com/salesforce-misc/ReVoman")
36 | inceptionYear.set("2023")
37 | licenses {
38 | license {
39 | name.set("The Apache License, Version 2.0")
40 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
41 | }
42 | }
43 | developers {
44 | developer {
45 | id.set("overfullstack")
46 | name.set("Gopal S Akshintala")
47 | email.set("gopalakshintala@gmail.com")
48 | }
49 | }
50 | scm {
51 | connection.set("scm:git:https://github.com/salesforce-misc/ReVoman")
52 | developerConnection.set("scm:git:git@github.com/salesforce-misc/ReVoman.git")
53 | url.set("https://github.com/salesforce-misc/ReVoman")
54 | }
55 | }
56 | }
57 | }
58 |
59 | signing { sign(publishing.publications["revoman"]) }
60 |
61 | tasks {
62 | javadoc {
63 | // TODO 22/05/21 gopala.akshintala: Turn this on after writing all javadocs
64 | isFailOnError = false
65 | options.encoding("UTF-8")
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/revoman.root-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | import com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA
9 | import com.diffplug.spotless.LineEnding.PLATFORM_NATIVE
10 | import com.diffplug.spotless.extra.wtp.EclipseWtpFormatterStep.XML
11 | import io.gitlab.arturbosch.detekt.Detekt
12 |
13 | plugins {
14 | id("com.diffplug.spotless")
15 | id("io.gitlab.arturbosch.detekt")
16 | id("com.adarshr.test-logger")
17 | }
18 |
19 | version = VERSION
20 |
21 | group = GROUP_ID
22 |
23 | description = "ReVoman - A Template-driven API automation tool for JVM (Java/Kotlin)"
24 |
25 | repositories {
26 | mavenCentral()
27 | maven("https://oss.sonatype.org/content/repositories/snapshots")
28 | }
29 |
30 | spotless {
31 | lineEndings = PLATFORM_NATIVE
32 | kotlin {
33 | ktfmt().googleStyle()
34 | target("src/*/kotlin/**/*.kt", "src/*/java/**/*.kt")
35 | trimTrailingWhitespace()
36 | endWithNewline()
37 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**")
38 | }
39 | kotlinGradle {
40 | ktfmt().googleStyle()
41 | trimTrailingWhitespace()
42 | endWithNewline()
43 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**")
44 | }
45 | java {
46 | toggleOffOn()
47 | target("src/*/java/**/*.java")
48 | importOrder()
49 | removeUnusedImports()
50 | googleJavaFormat()
51 | trimTrailingWhitespace()
52 | leadingSpacesToTabs(2)
53 | endWithNewline()
54 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**")
55 | }
56 | format("documentation") {
57 | target("*.md", "*.adoc")
58 | trimTrailingWhitespace()
59 | leadingSpacesToTabs(2)
60 | endWithNewline()
61 | }
62 | }
63 |
64 | detekt {
65 | parallel = true
66 | buildUponDefaultConfig = true
67 | baseline = file("$rootDir/detekt/baseline.xml")
68 | config.setFrom(file("$rootDir/detekt/config.yml"))
69 | }
70 |
71 | testlogger.theme = MOCHA
72 |
73 | tasks.withType().configureEach { reports { xml.required.set(true) } }
74 |
--------------------------------------------------------------------------------
/detekt/baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TooManyFunctions:ResponseConfig.kt$ResponseConfig$Companion
5 | TooManyFunctions:PostmanEnvironment.kt$PostmanEnvironment<ValueT : Any?> : MutableMap
6 | TooManyFunctions:ExeUtils.kt$com.salesforce.revoman.internal.exe.ExeUtils.kt
7 | LongMethod:ReVoman.kt$ReVoman$private fun executeStepsSerially( pmStepsFlattened: List<Pair<Step, Item>>, kick: Kick, moshiReVoman: MoshiReVoman, ): List<StepReport>
8 | SpreadOperator:Step.kt$Step$(*FOLDER_DELIMITER.toCharArray())
9 | SpreadOperator:CaseInsensitiveEnumAdapter.kt$CaseInsensitiveEnumAdapter$(*nameStrings.toTypedArray())
10 | TooManyFunctions:JsonWriterUtils.kt$com.salesforce.revoman.input.json.JsonWriterUtils.kt
11 | TooManyFunctions:JsonReaderUtils.kt$com.salesforce.revoman.input.json.JsonReaderUtils.kt
12 | TooManyFunctions:KickDef.kt$KickDef
13 | MagicNumber:Constants.kt$400
14 | MagicNumber:Constants.kt$499
15 | MagicNumber:Constants.kt$200
16 | MagicNumber:Constants.kt$299
17 | FunctionNaming:RestfulAPIDevKtTest.kt$RestfulAPIDevKtTest$@Test fun `execute restful-api dev pm collection`()
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/detekt/config.yml:
--------------------------------------------------------------------------------
1 | build:
2 | maxIssues: 999
3 | comments:
4 | active: false
5 | style:
6 | MaxLineLength:
7 | active: false
8 |
--------------------------------------------------------------------------------
/docs/images/cognitive-complexity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/cognitive-complexity.png
--------------------------------------------------------------------------------
/docs/images/failure-hierarchy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/failure-hierarchy.png
--------------------------------------------------------------------------------
/docs/images/hybrid-tool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/hybrid-tool.png
--------------------------------------------------------------------------------
/docs/images/manual-to-automation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/manual-to-automation.png
--------------------------------------------------------------------------------
/docs/images/mutable-env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/mutable-env.png
--------------------------------------------------------------------------------
/docs/images/node_modules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/node_modules.png
--------------------------------------------------------------------------------
/docs/images/postman-run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/postman-run.png
--------------------------------------------------------------------------------
/docs/images/pq-exe-logging.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/pq-exe-logging.gif
--------------------------------------------------------------------------------
/docs/images/pq-revoman-test-time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/pq-revoman-test-time.png
--------------------------------------------------------------------------------
/docs/images/resfulapi-dev-pm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/resfulapi-dev-pm.png
--------------------------------------------------------------------------------
/docs/images/revoman-demo-thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/revoman-demo-thumbnail.png
--------------------------------------------------------------------------------
/docs/images/rundown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/rundown.png
--------------------------------------------------------------------------------
/docs/images/step-procedure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/step-procedure.png
--------------------------------------------------------------------------------
/docs/images/step-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/docs/images/step-report.png
--------------------------------------------------------------------------------
/docs/release-notes/revoman-2.0.adoc:
--------------------------------------------------------------------------------
1 | = Key Features
2 |
3 | * Core compatibility
4 | ** Persona-based user creation and test-utils to recalculate PSG
5 | ** Async operation Support
6 | * Pre- / Post-Hooks can be configured for each step, which can act as callbacks, especially useful for Async operations
7 | * Enhance Input config
8 | ** Add parameters like runOnlyStep, skipSteps, haltOnAnyErrorExceptSteps etc.
9 | * Enhance variable support
10 | ** Custom Dynamic variables for use cases like Parametric testing
11 | ** Add Recursive variable support both for Template and Environment
12 | ** Add more dynamic variables like $epoch, $randomFutureDate etc.
13 | * Enhance JS support for Test scripts.
14 | * Enhance error handling
15 | * Step name is represented through its full folder path
16 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
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 | org.gradle.parallel=true
9 | org.gradle.caching=true
10 | org.gradle.configuration-cache=true
11 | org.gradle.configuration-cache.parallel=true
12 | org.gradle.configuration-cache.problems=warn
13 | org.gradle.jvmargs=-XX:+UseParallelGC
14 |
15 | moshix.generateProguardRules=false
16 |
17 | kapt.classloaders.cache.size=1
18 | kapt.include.compile.classpath=false
19 | kapt.incremental.apt=false
20 | kapt.use.k2=true
21 |
22 | kotest.framework.classpath.scanning.autoscan.disable=true
23 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/salesforce-misc/ReVoman/f792f020b21e5b4b8c20367c62d5feaa4b1ad9ca/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/js/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "events": "^3.3.0",
9 | "lodash": "^4.17.21",
10 | "moment": "2.30.1",
11 | "stream": "^0.0.3",
12 | "timers": "^0.1.1",
13 | "xml2js": "^0.6.2"
14 | }
15 | },
16 | "node_modules/component-emitter": {
17 | "version": "2.0.0",
18 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-2.0.0.tgz",
19 | "integrity": "sha512-4m5s3Me2xxlVKG9PkZpQqHQR7bgpnN7joDMJ4yvVkVXngjoITG76IaZmzmywSeRTeTpc6N6r3H3+KyUurV8OYw==",
20 | "license": "MIT",
21 | "engines": {
22 | "node": ">=18"
23 | },
24 | "funding": {
25 | "url": "https://github.com/sponsors/sindresorhus"
26 | }
27 | },
28 | "node_modules/events": {
29 | "version": "3.3.0",
30 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
31 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
32 | "license": "MIT",
33 | "engines": {
34 | "node": ">=0.8.x"
35 | }
36 | },
37 | "node_modules/lodash": {
38 | "version": "4.17.21",
39 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
40 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
41 | "license": "MIT"
42 | },
43 | "node_modules/moment": {
44 | "version": "2.30.1",
45 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
46 | "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
47 | "license": "MIT",
48 | "engines": {
49 | "node": "*"
50 | }
51 | },
52 | "node_modules/sax": {
53 | "version": "1.4.1",
54 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
55 | "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
56 | "license": "ISC"
57 | },
58 | "node_modules/stream": {
59 | "version": "0.0.3",
60 | "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.3.tgz",
61 | "integrity": "sha512-aMsbn7VKrl4A2T7QAQQbzgN7NVc70vgF5INQrBXqn4dCXN1zy3L9HGgLO5s7PExmdrzTJ8uR/27aviW8or8/+A==",
62 | "license": "MIT",
63 | "dependencies": {
64 | "component-emitter": "^2.0.0"
65 | }
66 | },
67 | "node_modules/timers": {
68 | "version": "0.1.1",
69 | "resolved": "https://registry.npmjs.org/timers/-/timers-0.1.1.tgz",
70 | "integrity": "sha512-pkJC8uIP/gxDHxNQUBUbjHyl6oZfT+ofn7tbaHW+CFIUjI+Q2MBbHcx1JSBQfhDaTcO9bNg328q0i7Vk5PismQ==",
71 | "license": "MIT"
72 | },
73 | "node_modules/xml2js": {
74 | "version": "0.6.2",
75 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
76 | "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
77 | "license": "MIT",
78 | "dependencies": {
79 | "sax": ">=0.6.0",
80 | "xmlbuilder": "~11.0.0"
81 | },
82 | "engines": {
83 | "node": ">=4.0.0"
84 | }
85 | },
86 | "node_modules/xmlbuilder": {
87 | "version": "11.0.1",
88 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
89 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
90 | "license": "MIT",
91 | "engines": {
92 | "node": ">=4.0"
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "events": "^3.3.0",
4 | "lodash": "^4.17.21",
5 | "moment": "2.30.1",
6 | "stream": "^0.0.3",
7 | "timers": "^0.1.1",
8 | "xml2js": "^0.6.2"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/qodana.yaml:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------#
2 | # Qodana analysis is configured by qodana.yaml file #
3 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html #
4 | #-------------------------------------------------------------------------------#
5 | version: "1.0"
6 |
7 | #Specify inspection profile for code analysis
8 | profile:
9 | name: qodana.recommended
10 |
11 | #Enable inspections
12 | #include:
13 | # - name:
14 |
15 | #Disable inspections
16 | exclude:
17 | - name: DataClassPrivateConstructor
18 | - name: UnusedSymbol
19 | # paths:
20 | # -
21 |
22 | projectJDK: '11' #(Applied in CI/CD pipeline)
23 |
24 | #Execute shell command before Qodana execution (Applied in CI/CD pipeline)
25 | #bootstrap: |+
26 | # update-alternatives --list java
27 | # java --version
28 | # ./gradlew kaptKotlin
29 |
30 | #Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
31 | #plugins:
32 | # - id: #(plugin id can be found at https://plugins.jetbrains.com)
33 |
34 | #Specify Qodana linter for analysis (Applied in CI/CD pipeline)
35 | linter: jetbrains/qodana-jvm:latest
36 |
37 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:recommended"
4 | ],
5 | "automerge": true,
6 | "commitBodyTable": true,
7 | "packageRules": [
8 | {
9 | "matchUpdateTypes": [
10 | "major",
11 | "minor",
12 | "patch"
13 | ],
14 | "groupName": "all dependencies",
15 | "groupSlug": "all"
16 | },
17 | {
18 | "matchPackageNames": [
19 | "org.graalvm.sdk:graal-sdk",
20 | "org.graalvm.js:js",
21 | "org.http4k:http4k-bom",
22 | "org.http4k:http4k-core",
23 | "org.http4k:http4k-client-apache",
24 | "org.http4k:http4k-format-moshi"
25 | ],
26 | "enabled": false
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | plugins { id("com.gradle.develocity") version "4.0.2" }
9 |
10 | dependencyResolutionManagement {
11 | versionCatalogs { create("libs") { from(files("libs.versions.toml")) } }
12 | pluginManagement {
13 | repositories {
14 | mavenCentral()
15 | gradlePluginPortal()
16 | google()
17 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots")
18 | maven("https://oss.sonatype.org/content/repositories/snapshots")
19 | }
20 | }
21 | }
22 |
23 | val isCI = !System.getenv("CI").isNullOrEmpty()
24 |
25 | develocity {
26 | buildScan {
27 | publishing.onlyIf {
28 | it.buildResult.failures.isNotEmpty() && !System.getenv("CI").isNullOrEmpty()
29 | }
30 | uploadInBackground.set(!isCI)
31 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use"
32 | termsOfUseAgree = "yes"
33 | }
34 | }
35 |
36 | rootProject.name = "revoman-root"
37 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/adapters/IDAdapter.java:
--------------------------------------------------------------------------------
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.adapters;
9 |
10 | import com.salesforce.revoman.integration.core.pq.connect.response.ID;
11 | import com.squareup.moshi.FromJson;
12 | import com.squareup.moshi.ToJson;
13 |
14 | // * NOTE 10 Mar 2024 gopala.akshintala: Custom Type Adapter
15 | public class IDAdapter {
16 | public static final IDAdapter INSTANCE = new IDAdapter();
17 |
18 | @FromJson
19 | ID fromJson(String id) {
20 | return new ID(id);
21 | }
22 |
23 | @ToJson
24 | String toJson(ID id) {
25 | return id.id();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/bt2bs/MilestoneBillingE2ETest.java:
--------------------------------------------------------------------------------
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.PERSONA_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 | PERSONA_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 | }
40 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/bt2bs/ReVomanConfigForBT2BS.java:
--------------------------------------------------------------------------------
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.ASSERT_COMPOSITE_RESPONSE_SUCCESS;
14 | import static com.salesforce.revoman.integration.core.CoreUtils.unmarshallCompositeGraphResponse;
15 | import static com.salesforce.revoman.integration.core.CoreUtils.unmarshallCompositeResponse;
16 | import static com.salesforce.revoman.output.ExeType.HTTP_STATUS;
17 |
18 | import com.salesforce.revoman.input.config.HookConfig;
19 | import com.salesforce.revoman.input.config.Kick;
20 | import com.salesforce.revoman.integration.core.adapters.IDAdapter;
21 |
22 | public final class ReVomanConfigForBT2BS {
23 |
24 | private ReVomanConfigForBT2BS() {}
25 |
26 | static final String ENV_PATH = "pm-templates/core/milestone/env.postman_environment.json";
27 | static final String IGNORE_HTTP_STATUS_UNSUCCESSFUL = "ignoreHTTPStatusUnsuccessful";
28 | static final String NODE_MODULE_RELATIVE_PATH = "js";
29 |
30 | // ## User creation and Setup
31 | static final String COLLECTION_PATH = "pm-templates/core/milestone/";
32 | private static final String PERSONA_CREATION_AND_SETUP_COLLECTION_PATH =
33 | COLLECTION_PATH + "persona-creation-and-setup.postman_collection.json";
34 | static final Kick PERSONA_CREATION_AND_SETUP_CONFIG =
35 | Kick.configure()
36 | .templatePath(PERSONA_CREATION_AND_SETUP_COLLECTION_PATH)
37 | .environmentPath(ENV_PATH)
38 | .responseConfig(unmarshallCompositeGraphResponse(), unmarshallCompositeResponse())
39 | .hooks(ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS, ASSERT_COMPOSITE_RESPONSE_SUCCESS)
40 | .nodeModulesPath(NODE_MODULE_RELATIVE_PATH)
41 | .haltOnFailureOfTypeExcept(
42 | HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
43 | .insecureHttp(true)
44 | .off();
45 |
46 | // ## Hooks
47 | static final String PST = "connect/rev/sales-transaction/actions/place";
48 | static final String STANDALONE_INVOICE_IA =
49 | "commerce/invoicing/invoices/collection/actions/generate";
50 | static final String ASSETIZE_IA = "actions/standard/createOrUpdateAssetFromOrder";
51 | static final String AMEND_API = "connect/revenue-management/assets/actions/amend";
52 | static final String CANCEL_API = "connect/revenue-management/assets/actions/cancel";
53 | public static final HookConfig MEMQ_AWAIT =
54 | post(
55 | afterStepContainingURIPathOfAny(
56 | PST, STANDALONE_INVOICE_IA, ASSETIZE_IA, AMEND_API, CANCEL_API),
57 | (ignore1, ignore2) -> Thread.sleep(5000));
58 |
59 | // ## Milestone Setup Config
60 | private static final String MB_SETUP_POSTMAN_COLLECTION_PATH =
61 | COLLECTION_PATH + "milestone-setup.postman_collection.json";
62 | static final Kick MILESTONE_SETUP_CONFIG =
63 | Kick.configure()
64 | .templatePath(MB_SETUP_POSTMAN_COLLECTION_PATH)
65 | .haltOnFailureOfTypeExcept(
66 | HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
67 | .responseConfig(unmarshallCompositeGraphResponse(), unmarshallCompositeResponse())
68 | .hooks(ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS, ASSERT_COMPOSITE_RESPONSE_SUCCESS)
69 | .haltOnFailureOfTypeExcept(
70 | HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
71 | .globalCustomTypeAdapter(IDAdapter.INSTANCE)
72 | .nodeModulesPath(NODE_MODULE_RELATIVE_PATH)
73 | .off();
74 |
75 | // ## Milestone Config
76 | private static final String MB_POSTMAN_COLLECTION_PATH =
77 | COLLECTION_PATH + "bmp-create-runtime.postman_collection.json";
78 | static final Kick MILESTONE_CONFIG =
79 | Kick.configure()
80 | .templatePath(MB_POSTMAN_COLLECTION_PATH)
81 | .responseConfig(unmarshallCompositeGraphResponse(), unmarshallCompositeResponse())
82 | .hooks(
83 | MEMQ_AWAIT,
84 | ASSERT_COMPOSITE_GRAPH_RESPONSE_SUCCESS,
85 | ASSERT_COMPOSITE_RESPONSE_SUCCESS)
86 | .haltOnFailureOfTypeExcept(
87 | HTTP_STATUS, afterStepContainingHeader(IGNORE_HTTP_STATUS_UNSUCCESSFUL))
88 | .globalCustomTypeAdapter(IDAdapter.INSTANCE)
89 | .nodeModulesPath(NODE_MODULE_RELATIVE_PATH)
90 | .off();
91 | }
92 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/ConnectInputRepresentationWithGraph.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | public interface ConnectInputRepresentationWithGraph {
11 |
12 | void setGraph(ObjectGraphInputRepresentation graph);
13 |
14 | ObjectGraphInputRepresentation getGraph();
15 | }
16 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/ObjectGraphInputRepresentation.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | public class ObjectGraphInputRepresentation {
11 |
12 | private String graphId;
13 |
14 | private ObjectWithReferenceInputRepresentationList records;
15 |
16 | private boolean isSetGraphId;
17 | private boolean isSetRecords;
18 |
19 | public String getGraphId() {
20 | return this.graphId;
21 | }
22 |
23 | public void setGraphId(String graphId) {
24 | this.graphId = graphId;
25 | this.isSetGraphId = true;
26 | }
27 |
28 | public ObjectWithReferenceInputRepresentationList getRecords() {
29 | return this.records;
30 | }
31 |
32 | public void setRecords(ObjectWithReferenceInputRepresentationList records) {
33 | this.records = records;
34 | this.isSetRecords = true;
35 | }
36 |
37 | public boolean _isSetGraphId() {
38 | return this.isSetGraphId;
39 | }
40 |
41 | public boolean _isSetRecords() {
42 | return this.isSetRecords;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/ObjectInputRepresentationMap.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | public class ObjectInputRepresentationMap {
14 |
15 | private Map recordBody;
16 | private boolean isSetRecordBody;
17 |
18 | public ObjectInputRepresentationMap() {
19 | this.recordBody = new HashMap<>();
20 | }
21 |
22 | public Map getRecordBody() {
23 | return this.recordBody;
24 | }
25 |
26 | public void setRecordBody(Map recordBody) {
27 | this.recordBody = recordBody;
28 | this.isSetRecordBody = true;
29 | }
30 |
31 | public boolean _isSetRecordBody() {
32 | return this.isSetRecordBody;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/ObjectWithReferenceInputRepresentation.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | public class ObjectWithReferenceInputRepresentation {
11 |
12 | private String referenceId;
13 | private ObjectInputRepresentationMap record;
14 |
15 | private boolean isSetReferenceId;
16 | private boolean isSetRecord;
17 |
18 | public ObjectInputRepresentationMap getRecord() {
19 | return this.record;
20 | }
21 |
22 | public void setRecord(ObjectInputRepresentationMap record) {
23 | this.record = record;
24 | this.isSetRecord = true;
25 | }
26 |
27 | public String getReferenceId() {
28 | return this.referenceId;
29 | }
30 |
31 | public void setReferenceId(String referenceId) {
32 | this.referenceId = referenceId;
33 | this.isSetReferenceId = true;
34 | }
35 |
36 | public boolean _isSetRecord() {
37 | return this.isSetRecord;
38 | }
39 |
40 | public boolean _isSetReferenceId() {
41 | return this.isSetReferenceId;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/ObjectWithReferenceInputRepresentationList.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | public class ObjectWithReferenceInputRepresentationList {
14 |
15 | private List recordsList;
16 | private boolean isSetRecordsList;
17 |
18 | public ObjectWithReferenceInputRepresentationList() {
19 | super();
20 | this.recordsList = new ArrayList<>();
21 | }
22 |
23 | public List getRecordsList() {
24 | return this.recordsList;
25 | }
26 |
27 | public void setRecordsList(List recordsList) {
28 | this.recordsList = recordsList;
29 | this.isSetRecordsList = true;
30 | }
31 |
32 | public boolean _isSetRecordsList() {
33 | return this.isSetRecordsList;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/PlaceQuoteInputRepresentation.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | public class PlaceQuoteInputRepresentation implements ConnectInputRepresentationWithGraph {
11 |
12 | private PricingPreferenceEnum pricingPref;
13 | private Boolean doAsync;
14 | private ObjectGraphInputRepresentation graph;
15 | private boolean isSetGraph;
16 |
17 | public Boolean getDoAsync() {
18 | return doAsync;
19 | }
20 |
21 | public ObjectGraphInputRepresentation getGraph() {
22 | return this.graph;
23 | }
24 |
25 | public void setGraph(ObjectGraphInputRepresentation graph) {
26 | this.graph = graph;
27 | this.isSetGraph = true;
28 | }
29 |
30 | public boolean _isSetGraph() {
31 | return this.isSetGraph;
32 | }
33 |
34 | private boolean isSetPricingPref;
35 |
36 | private boolean isSetDoAsync;
37 |
38 | public PricingPreferenceEnum getPricingPref() {
39 | return pricingPref;
40 | }
41 |
42 | public void setPricingPref(PricingPreferenceEnum pricingPref) {
43 | this.pricingPref = pricingPref;
44 | this.isSetPricingPref = true;
45 | }
46 |
47 | public void setDoAsync(Boolean doAsync) {
48 | this.doAsync = doAsync;
49 | this.isSetDoAsync = true;
50 | }
51 |
52 | public boolean _isSetPricingPref() {
53 | return this.isSetPricingPref;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/request/PricingPreferenceEnum.java:
--------------------------------------------------------------------------------
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.pq.connect.request;
9 |
10 | public enum PricingPreferenceEnum {
11 | Force,
12 | Skip,
13 | System;
14 | }
15 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/response/ID.java:
--------------------------------------------------------------------------------
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.pq.connect.response;
9 |
10 | public record ID(String id) {}
11 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/response/PlaceQuoteErrorResponseRepresentation.java:
--------------------------------------------------------------------------------
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.pq.connect.response;
9 |
10 | public class PlaceQuoteErrorResponseRepresentation {
11 | private final String referenceId;
12 | private final String errorCode;
13 | private final String message;
14 |
15 | public PlaceQuoteErrorResponseRepresentation(
16 | final String referenceId, final String errorCode, final String message) {
17 | super();
18 | this.referenceId = referenceId;
19 | this.errorCode = errorCode;
20 | this.message = message;
21 | }
22 |
23 | public String getMessage() {
24 | return message;
25 | }
26 |
27 | public String getErrorCode() {
28 | return errorCode;
29 | }
30 |
31 | public String getReferenceId() {
32 | return referenceId;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/core/pq/connect/response/PlaceQuoteOutputRepresentation.java:
--------------------------------------------------------------------------------
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.pq.connect.response;
9 |
10 | import java.util.List;
11 |
12 | public class PlaceQuoteOutputRepresentation {
13 | private final String requestIdentifier;
14 | private String statusURL;
15 | private final ID quoteId;
16 | private final Boolean success;
17 | private final List responseError;
18 |
19 | public PlaceQuoteOutputRepresentation(
20 | String requestIdentifier,
21 | String statusURL,
22 | ID quoteId,
23 | Boolean success,
24 | List responseError) {
25 | super();
26 | this.requestIdentifier = requestIdentifier;
27 | this.statusURL = statusURL;
28 | this.success = success;
29 | this.responseError = responseError;
30 | this.quoteId = quoteId;
31 | }
32 |
33 | public String getRequestIdentifier() {
34 | return this.requestIdentifier;
35 | }
36 |
37 | public String getStatusURL() {
38 | return this.statusURL;
39 | }
40 |
41 | public void setStatusURL(String statusURL) {
42 | this.statusURL = statusURL;
43 | }
44 |
45 | public Boolean getSuccess() {
46 | return this.success;
47 | }
48 |
49 | public List getResponseError() {
50 | return this.responseError;
51 | }
52 |
53 | public ID getQuoteId() {
54 | return quoteId;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/integrationTest/java/com/salesforce/revoman/integration/restfulapidev/RestfulAPIDevTest.java:
--------------------------------------------------------------------------------
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.restfulapidev;
9 |
10 | import static com.google.common.truth.Truth.assertThat;
11 |
12 | import com.salesforce.revoman.ReVoman;
13 | import com.salesforce.revoman.input.config.Kick;
14 | import org.junit.jupiter.api.DisplayName;
15 | import org.junit.jupiter.api.Test;
16 |
17 | class RestfulAPIDevTest {
18 | private static final String PM_COLLECTION_PATH =
19 | "pm-templates/restfulapidev/restful-api.dev.postman_collection.json";
20 | private static final String PM_ENVIRONMENT_PATH =
21 | "pm-templates/restfulapidev/restful-api.dev.postman_environment.json";
22 |
23 | // tag::revoman-simple-demo[]
24 | @Test
25 | @DisplayName("restful-api.dev")
26 | void restfulApiDev() {
27 | final var rundown =
28 | ReVoman.revUp( // <1>
29 | Kick.configure()
30 | .templatePath(PM_COLLECTION_PATH) // <2>
31 | .environmentPath(PM_ENVIRONMENT_PATH) // <3>
32 | .nodeModulesPath("js")
33 | .off());
34 | assertThat(rundown.firstUnIgnoredUnsuccessfulStepReport()).isNull(); // <4>
35 | assertThat(rundown.stepReports).hasSize(4); // <5>
36 | }
37 | // end::revoman-simple-demo[]
38 | }
39 |
--------------------------------------------------------------------------------
/src/integrationTest/kotlin/com/salesforce/revoman/integration/apigee/ApigeeKtTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.apigee
9 |
10 | import com.google.common.truth.Truth.assertThat
11 | import com.salesforce.revoman.ReVoman
12 | import com.salesforce.revoman.input.config.Kick
13 | import org.junit.jupiter.api.Test
14 |
15 | class ApigeeKtTest {
16 | @Test
17 | fun `xml2js apigee`() {
18 | val rundown =
19 | ReVoman.revUp(Kick.configure().templatePath(PM_COLLECTION_PATH).nodeModulesPath("js").off())
20 | assertThat(rundown.stepReports).hasSize(1)
21 | assertThat(rundown.firstUnsuccessfulStepReport).isNull()
22 | assertThat(rundown.mutableEnv["city"]).isEqualTo("San Jose")
23 | }
24 |
25 | companion object {
26 | private const val PM_COLLECTION_PATH = "pm-templates/apigee/apigee.postman_collection.json"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/integrationTest/kotlin/com/salesforce/revoman/integration/restfulapidev/RestfulAPIDevKtTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.restfulapidev
9 |
10 | import com.google.common.truth.Truth.assertThat
11 | import com.salesforce.revoman.ReVoman
12 | import com.salesforce.revoman.input.config.Kick
13 | import org.junit.jupiter.api.Test
14 |
15 | class RestfulAPIDevKtTest {
16 | @Test
17 | fun `execute restful-api dev pm collection`() {
18 | val rundown =
19 | ReVoman.revUp(
20 | // <1>
21 | Kick.configure()
22 | .templatePath(PM_COLLECTION_PATH) // <2>
23 | .environmentPath(PM_ENVIRONMENT_PATH) // <3>
24 | .nodeModulesPath("js")
25 | .off()
26 | )
27 | assertThat(rundown.firstUnsuccessfulStepReport).isNull()
28 | assertThat(rundown.stepReports).hasSize(4)
29 | }
30 |
31 | companion object {
32 | private const val PM_COLLECTION_PATH =
33 | "pm-templates/restfulapidev/restful-api.dev.postman_collection.json"
34 | private const val PM_ENVIRONMENT_PATH =
35 | "pm-templates/restfulapidev/restful-api.dev.postman_environment.json"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/json/pq-payload.json:
--------------------------------------------------------------------------------
1 | {
2 | "pricingPref" : "System",
3 | "doAsync" : true,
4 | "graph" : {
5 | "graphId" : "pq-create-with-bundles",
6 | "records" : [
7 | {
8 | "referenceId" : "refQuote",
9 | "record" : {
10 | "attributes" : {
11 | "type" : "Quote",
12 | "method" : "POST"
13 | },
14 | "Name" : "Quote_{{$randomCompanyName}}",
15 | "OpportunityId" : "{{opportunityId}}"
16 | }
17 | },
18 | {
19 | "referenceId" : "refQuoteItem1",
20 | "record" : {
21 | "attributes" : {
22 | "type" : "QuoteLineItem",
23 | "method" : "POST"
24 | },
25 | "QuoteId" : "@{refQuote.id}",
26 | "PricebookEntryId" : "{{evergreenPriceBookEntryId}}",
27 | "Product2Id" : "{{evergreenProductId}}",
28 | "Quantity" : 2.0,
29 | "UnitPrice" : 25.0,
30 | "PeriodBoundary" : "Anniversary",
31 | "BillingFrequency" : "Monthly",
32 | "StartDate" : "{{$currentDate}}"
33 | }
34 | },
35 | {
36 | "referenceId" : "refQuoteItem2",
37 | "record" : {
38 | "attributes" : {
39 | "type" : "QuoteLineItem",
40 | "method" : "POST"
41 | },
42 | "QuoteId" : "@{refQuote.id}",
43 | "PricebookEntryId" : "{{termedPriceBookEntryId}}",
44 | "Product2Id" : "{{termedProductId}}",
45 | "Quantity" : 2.0,
46 | "UnitPrice" : 25.0,
47 | "EndDate" : "{{$randomFutureDate}}",
48 | "PeriodBoundary" : "DayOfPeriod",
49 | "PeriodBoundaryDay" : 4,
50 | "BillingFrequency" : "Monthly",
51 | "StartDate" : "{{$currentDate}}"
52 | }
53 | },
54 | {
55 | "referenceId" : "refQuoteItem3",
56 | "record" : {
57 | "attributes" : {
58 | "type" : "QuoteLineItem",
59 | "method" : "POST"
60 | },
61 | "QuoteId" : "@{refQuote.id}",
62 | "PricebookEntryId" : "{{oneTimePriceBookEntryId}}",
63 | "Product2Id" : "{{oneTimeProductId}}",
64 | "Quantity" : 2.0,
65 | "UnitPrice" : 25.0,
66 | "StartDate" : "{{$currentDate}}",
67 | "BillingFrequency" : null,
68 | "PeriodBoundary" : null
69 | }
70 | },
71 | {
72 | "referenceId" : "refQuoteItem4",
73 | "record" : {
74 | "attributes" : {
75 | "type" : "QuoteLineItem",
76 | "method" : "POST"
77 | },
78 | "QuoteId" : "@{refQuote.id}",
79 | "PricebookEntryId" : "{{oneTimePriceBookEntryId}}",
80 | "Product2Id" : "{{oneTimeProductId}}",
81 | "Quantity" : 2.0,
82 | "UnitPrice" : 25.0,
83 | "StartDate" : "{{$currentDate}}"
84 | }
85 | },
86 | {
87 | "referenceId" : "refQLR1",
88 | "record" : {
89 | "attributes" : {
90 | "type" : "QuoteLineRelationship",
91 | "method" : "POST"
92 | },
93 | "MainQuoteLineId" : "@{refQuoteItem1.id}",
94 | "AssociatedQuoteLineId" : "@{refQuoteItem4.id}",
95 | "ProductRelationshipTypeId" : "{{productRelationshipTypeId}}",
96 | "AssociatedQuoteLinePricing" : "IncludedInBundlePrice"
97 | }
98 | }
99 | ]
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/json/pq-response.json:
--------------------------------------------------------------------------------
1 | {
2 | "quoteId": "0Q0xx0000004E2mCAE",
3 | "requestIdentifier": "95Txx0000004E60",
4 | "responseError": [],
5 | "statusURL": "/services/data/v58.0/sobjects/RevenueAsyncOperation/95Txx0000004E60EAE",
6 | "success": true
7 | }
--------------------------------------------------------------------------------
/src/integrationTest/resources/json/pricing-pref.json:
--------------------------------------------------------------------------------
1 | {
2 | "pricingPref" : "SYSTEM"
3 | }
4 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/apigee/apigee.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "00262e9b-48de-4d72-bea9-5db5c77029cb",
4 | "name": "Apigee",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
6 | "_exporter_id": "23827434"
7 | },
8 | "item": [
9 | {
10 | "name": "Apigee",
11 | "event": [
12 | {
13 | "listen": "prerequest",
14 | "script": {
15 | "exec": [
16 | ""
17 | ],
18 | "type": "text/javascript",
19 | "packages": {}
20 | }
21 | },
22 | {
23 | "listen": "test",
24 | "script": {
25 | "exec": [
26 | "var xml2js = require('xml2js')",
27 | "xml2js.parseString(pm.response.text(), { explicitArray: false }, (_, jsonResponse) => {",
28 | " let result = jsonResponse.root.city",
29 | " pm.environment.set(\"city\", result)",
30 | "})"
31 | ],
32 | "type": "text/javascript",
33 | "packages": {}
34 | }
35 | }
36 | ],
37 | "request": {
38 | "method": "GET",
39 | "header": [],
40 | "url": {
41 | "raw": "https://mocktarget.apigee.net/xml",
42 | "protocol": "https",
43 | "host": [
44 | "mocktarget",
45 | "apigee",
46 | "net"
47 | ],
48 | "path": [
49 | "xml"
50 | ]
51 | }
52 | },
53 | "response": []
54 | }
55 | ]
56 | }
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/core/milestone/env.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "eca3a642-ef81-42ae-9e29-e9e5065c2390",
3 | "name": "bt2bs",
4 | "values": [
5 | {
6 | "key" : "baseUrl",
7 | "value" : "https://dlt000005y8w62ai.test1.my.pc-rnd.salesforce.com",
8 | "type" : "default",
9 | "enabled" : true
10 | },
11 | {
12 | "key" : "username",
13 | "value" : "asisrenewal@salesforce.com",
14 | "type" : "default",
15 | "enabled" : true
16 | },
17 | {
18 | "key" : "password",
19 | "value" : "test1234",
20 | "type" : "default",
21 | "enabled" : true
22 | },
23 | {
24 | "key": "commonUserPassword",
25 | "value": "test1234",
26 | "type": "default",
27 | "enabled": true
28 | },
29 | {
30 | "key": "$unitPrice",
31 | "value": "63.00",
32 | "type": "default",
33 | "enabled": true
34 | },
35 | {
36 | "key": "secretToken-admin",
37 | "value": "",
38 | "type": "default",
39 | "enabled": true
40 | },
41 | {
42 | "key": "secretToken",
43 | "value": "",
44 | "type": "default",
45 | "enabled": true
46 | },
47 | {
48 | "key": "secretToken-billingAdmin",
49 | "value": "",
50 | "type": "default",
51 | "enabled": true
52 | },
53 | {
54 | "key": "secretToken-taxAdmin",
55 | "value": "",
56 | "type": "default",
57 | "enabled": true
58 | },
59 | {
60 | "key": "secretToken-productAndPricingAdmin",
61 | "value": "",
62 | "type": "default",
63 | "enabled": true
64 | },
65 | {
66 | "key": "secretToken-salesRep",
67 | "value": "",
68 | "type": "default",
69 | "enabled": true
70 | }
71 | ],
72 | "_postman_variable_scope": "environment",
73 | "_postman_exported_at": "2022-07-28T10:35:45.674Z",
74 | "_postman_exported_using": "Postman/9.20.0-canary"
75 | }
76 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/core/pq/pq-env.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "eca3a642-ef81-42ae-9e29-e9e5065c2390",
3 | "name" : "pq",
4 | "values" : [
5 | {
6 | "key" : "baseUrl",
7 | "value" : "https://gopalaaksh-wsl3:6101",
8 | "type" : "default",
9 | "enabled" : true
10 | },
11 | {
12 | "key" : "username",
13 | "value" : "linux-sm@252.org",
14 | "type" : "default",
15 | "enabled" : true
16 | },
17 | {
18 | "key" : "password",
19 | "value" : "123456",
20 | "type" : "default",
21 | "enabled" : true
22 | },
23 | {
24 | "key" : "commonUserPassword",
25 | "value" : "test1234",
26 | "type" : "default",
27 | "enabled" : true
28 | },
29 | {
30 | "key" : "secretToken-admin",
31 | "value" : "",
32 | "type" : "default",
33 | "enabled" : true
34 | },
35 | {
36 | "key" : "secretToken-billingAdmin",
37 | "value" : "",
38 | "type" : "default",
39 | "enabled" : true
40 | },
41 | {
42 | "key" : "secretToken-taxAdmin",
43 | "value" : "",
44 | "type" : "default",
45 | "enabled" : true
46 | },
47 | {
48 | "key" : "secretToken-productAndPricingAdmin",
49 | "value" : "",
50 | "type" : "default",
51 | "enabled" : true
52 | },
53 | {
54 | "key" : "secretToken-salesRep",
55 | "value" : "",
56 | "type" : "default",
57 | "enabled" : true
58 | }
59 | ],
60 | "_postman_variable_scope" : "environment",
61 | "_postman_exported_at" : "2022-07-28T10:35:45.674Z",
62 | "_postman_exported_using" : "Postman/9.20.0-canary"
63 | }
64 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/pokemon/pokemon.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "535cddb4-a44b-4237-bf59-f27c334158af",
3 | "name" : "Pokemon",
4 | "values" : [
5 | {
6 | "key" : "baseUrl",
7 | "value" : "https://pokeapi.co/api/v2",
8 | "type" : "default",
9 | "enabled" : true
10 | }
11 | ],
12 | "_postman_variable_scope" : "environment",
13 | "_postman_exported_at" : "2022-03-31T10:46:04.026Z",
14 | "_postman_exported_using" : "Postman/9.12.2-canary"
15 | }
16 |
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/restfulapidev/restful-api.dev.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "8e2843e3-f3e9-4c55-a49d-89429fd02a83",
4 | "name": "restful-api.dev",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
6 | "_exporter_id": "23827434"
7 | },
8 | "item": [
9 | {
10 | "name": "all-objects",
11 | "event": [
12 | {
13 | "listen": "prerequest",
14 | "script": {
15 | "exec": [
16 | ""
17 | ],
18 | "type": "text/javascript"
19 | }
20 | }
21 | ],
22 | "request": {
23 | "method": "GET",
24 | "header": [],
25 | "url": {
26 | "raw": "https://{{uri}}/objects",
27 | "protocol": "https",
28 | "host": [
29 | "{{uri}}"
30 | ],
31 | "path": [
32 | "objects"
33 | ]
34 | }
35 | },
36 | "response": []
37 | },
38 | {
39 | "name": "add-object",
40 | "event": [
41 | {
42 | "listen": "test",
43 | "script": {
44 | "exec": [
45 | "var responseJson = pm.response.json();",
46 | "pm.environment.set(\"objId\", responseJson.id);",
47 | "pm.environment.set(\"data\", responseJson.data)",
48 | "pm.environment.set(\"productName\", responseJson.name)"
49 | ],
50 | "type": "text/javascript",
51 | "packages": {}
52 | }
53 | },
54 | {
55 | "listen": "prerequest",
56 | "script": {
57 | "exec": [
58 | "var moment = require('moment')",
59 | "var _ = require('lodash')",
60 | "",
61 | "pm.environment.set(\"$currentYear\", moment().year())",
62 | "pm.environment.set(\"$randomPrice\", _.random(1, 1000))"
63 | ],
64 | "type": "text/javascript",
65 | "packages": {}
66 | }
67 | }
68 | ],
69 | "request": {
70 | "method": "POST",
71 | "header": [],
72 | "body": {
73 | "mode": "raw",
74 | "raw": "{\n \"name\": \"{{$randomProduct}}\", // Dynamic variable\n \"data\": {\n \"year\": {{$currentYear}}, // Variable set via Pre-req\n \"price\": {{$randomPrice}}, // Variable set via Pre-req\n \"CPU model\": null\n }\n}",
75 | "options": {
76 | "raw": {
77 | "language": "json"
78 | }
79 | }
80 | },
81 | "url": {
82 | "raw": "https://{{uri}}/objects",
83 | "protocol": "https",
84 | "host": [
85 | "{{uri}}"
86 | ],
87 | "path": [
88 | "objects"
89 | ]
90 | }
91 | },
92 | "response": []
93 | },
94 | {
95 | "name": "update-object",
96 | "event": [
97 | {
98 | "listen": "test",
99 | "script": {
100 | "exec": [
101 | "var responseJson = pm.response.json();",
102 | "pm.environment.set(\"objId\", responseJson.id);",
103 | "pm.environment.set(\"data\", responseJson.data)"
104 | ],
105 | "type": "text/javascript",
106 | "packages": {}
107 | }
108 | },
109 | {
110 | "listen": "prerequest",
111 | "script": {
112 | "exec": [
113 | "var moment = require('moment')",
114 | "var _ = require('lodash')",
115 | "",
116 | "pm.environment.set(\"$currentYear\", moment().year())",
117 | "pm.environment.set(\"$randomPrice\", _.random(1, 1000))"
118 | ],
119 | "type": "text/javascript",
120 | "packages": {}
121 | }
122 | }
123 | ],
124 | "request": {
125 | "method": "PATCH",
126 | "header": [],
127 | "body": {
128 | "mode": "raw",
129 | "raw": "{\n \"name\": \"updated - {{productName}}\" // Update Name\n}",
130 | "options": {
131 | "raw": {
132 | "language": "json"
133 | }
134 | }
135 | },
136 | "url": {
137 | "raw": "https://{{uri}}/objects/{{objId}}",
138 | "protocol": "https",
139 | "host": [
140 | "{{uri}}"
141 | ],
142 | "path": [
143 | "objects",
144 | "{{objId}}"
145 | ]
146 | }
147 | },
148 | "response": []
149 | },
150 | {
151 | "name": "get-object-by-id",
152 | "event": [
153 | {
154 | "listen": "prerequest",
155 | "script": {
156 | "exec": [
157 | "console.log(pm.environment.get(\"data\"))"
158 | ],
159 | "type": "text/javascript",
160 | "packages": {}
161 | }
162 | }
163 | ],
164 | "request": {
165 | "method": "GET",
166 | "header": [],
167 | "url": {
168 | "raw": "https://{{uri}}/objects/{{objId}}",
169 | "protocol": "https",
170 | "host": [
171 | "{{uri}}"
172 | ],
173 | "path": [
174 | "objects",
175 | "{{objId}}"
176 | ]
177 | }
178 | },
179 | "response": []
180 | }
181 | ]
182 | }
--------------------------------------------------------------------------------
/src/integrationTest/resources/pm-templates/restfulapidev/restful-api.dev.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "cf14fd0d-74be-4069-b526-0c89d19c18bc",
3 | "name" : "restful-api.dev",
4 | "values" : [
5 | {
6 | "key" : "uri",
7 | "value" : "api.restful-api.dev",
8 | "type" : "default",
9 | "enabled" : true
10 | }
11 | ],
12 | "_postman_variable_scope" : "environment",
13 | "_postman_exported_at" : "2024-01-19T12:59:06.198Z",
14 | "_postman_exported_using" : "Postman/10.19.15-canary01"
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/FileUtils.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | @file:JvmName("FileUtils")
9 |
10 | package com.salesforce.revoman.input
11 |
12 | import java.io.File
13 | import java.io.InputStream
14 | import okio.BufferedSource
15 | import okio.FileSystem.Companion.RESOURCES
16 | import okio.FileSystem.Companion.SYSTEM
17 | import okio.Path.Companion.toPath
18 | import okio.buffer
19 | import okio.source
20 |
21 | fun bufferFile(filePath: String): BufferedSource =
22 | filePath.toPath().let { (if (it.isAbsolute) SYSTEM else RESOURCES).source(it).buffer() }
23 |
24 | fun readFileToString(filePath: String): String = bufferFile(filePath).readUtf8()
25 |
26 | fun bufferInputStream(inputStream: InputStream): BufferedSource = inputStream.source().buffer()
27 |
28 | fun readInputStreamToString(inputStream: InputStream): String =
29 | bufferInputStream(inputStream).readUtf8()
30 |
31 | fun bufferFile(file: File): BufferedSource = file.source().buffer()
32 |
33 | fun readFileToString(file: File): String = bufferFile(file).readUtf8()
34 |
35 | fun writeToFile(filePath: String, content: String) =
36 | SYSTEM.write(filePath.toPath()) { writeUtf8(content) }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/PostExeHook.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input
9 |
10 | import com.salesforce.revoman.output.Rundown
11 |
12 | fun interface PostExeHook {
13 | @Throws(Throwable::class) fun accept(currentRundown: Rundown, rundowns: List)
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/config/HookConfig.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.config
9 |
10 | import com.salesforce.revoman.input.config.HookConfig.StepHook.PostStepHook
11 | import com.salesforce.revoman.input.config.HookConfig.StepHook.PreStepHook
12 | import com.salesforce.revoman.input.config.StepPick.PostTxnStepPick
13 | import com.salesforce.revoman.input.config.StepPick.PreTxnStepPick
14 | import com.salesforce.revoman.output.Rundown
15 | import com.salesforce.revoman.output.report.Step
16 | import com.salesforce.revoman.output.report.StepReport
17 | import com.salesforce.revoman.output.report.TxnInfo
18 | import org.http4k.core.Request
19 |
20 | @ExposedCopyVisibility
21 | data class HookConfig private constructor(val pick: StepPick, val stepHook: StepHook) {
22 | sealed interface StepHook {
23 | fun interface PreStepHook : StepHook {
24 | @Throws(Throwable::class)
25 | fun accept(currentStep: Step, requestInfo: TxnInfo, rundown: Rundown)
26 | }
27 |
28 | fun interface PostStepHook : StepHook {
29 | @Throws(Throwable::class) fun accept(currentStepReport: StepReport, rundown: Rundown)
30 | }
31 | }
32 |
33 | companion object {
34 | @JvmStatic fun pre(pick: PreTxnStepPick, hook: PreStepHook): HookConfig = HookConfig(pick, hook)
35 |
36 | @JvmStatic
37 | fun post(pick: PostTxnStepPick, hook: PostStepHook): HookConfig = HookConfig(pick, hook)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/config/KickDef.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.config
9 |
10 | import com.salesforce.revoman.input.config.StepPick.ExeStepPick
11 | import com.salesforce.revoman.input.config.StepPick.PostTxnStepPick
12 | import com.salesforce.revoman.input.config.StepPick.PreTxnStepPick
13 | import com.salesforce.revoman.output.ExeType
14 | import com.salesforce.revoman.output.Rundown
15 | import com.salesforce.revoman.output.report.StepReport
16 | import com.squareup.moshi.JsonAdapter
17 | import com.squareup.moshi.JsonAdapter.Factory
18 | import io.vavr.control.Either
19 | import java.io.InputStream
20 | import java.lang.reflect.Type
21 | import java.util.Collections.disjoint
22 | import org.immutables.value.Value
23 | import org.immutables.value.Value.Style.ImplementationVisibility.PUBLIC
24 |
25 | @Config
26 | @Value.Immutable
27 | internal interface KickDef {
28 | // * NOTE 29/10/23 gopala.akshintala: `List` coz it allows adding a template twice,
29 | // as there can be use-cases to execute the same template twice
30 | fun templatePaths(): List
31 |
32 | fun templateInputStreams(): List
33 |
34 | fun environmentPaths(): Set
35 |
36 | fun environmentInputStreams(): List
37 |
38 | fun dynamicEnvironment(): Map
39 |
40 | fun nodeModulesPath(): String?
41 |
42 | fun customDynamicVariableGenerators(): Map
43 |
44 | @Value.Default fun haltOnAnyFailure(): Boolean = false
45 |
46 | fun haltOnFailureOfTypeExcept(): Map
47 |
48 | fun runOnlySteps(): List
49 |
50 | fun skipSteps(): List
51 |
52 | fun hooks(): List
53 |
54 | @Value.Derived fun preStepHooks(): List = hooks().filter { it.pick is PreTxnStepPick }
55 |
56 | @Value.Derived
57 | fun postStepHooks(): List = hooks().filter { it.pick is PostTxnStepPick }
58 |
59 | // * NOTE 29/10/23 gopala.akshintala: requestConfig/responseConfig are decoupled from pre-step /
60 | // post-step hook to allow setting up unmarshalling to strong types on the final rundown for
61 | // post-execution assertions agnostic of whether the step has any hooks
62 |
63 | fun requestConfig(): Set
64 |
65 | @Value.Derived
66 | fun customTypeAdaptersFromRequestConfig(): Map, Factory>> =
67 | requestConfig()
68 | .filter { it.customTypeAdapter != null }
69 | .associate { it.requestType to it.customTypeAdapter!! }
70 |
71 | fun responseConfig(): Set
72 |
73 | @Value.Derived
74 | fun pickToResponseConfig(): Map> =
75 | responseConfig().groupBy { it.ifSuccess }
76 |
77 | @Value.Derived
78 | fun customTypeAdaptersFromResponseConfig(): Map, Factory>> =
79 | responseConfig()
80 | .filter { it.customTypeAdapter != null }
81 | .associate { it.responseType to it.customTypeAdapter!! }
82 |
83 | fun globalCustomTypeAdapters(): List
84 |
85 | fun globalSkipTypes(): Set>
86 |
87 | @Value.Default fun insecureHttp(): Boolean = false
88 |
89 | @Value.Check
90 | fun validateConfig() {
91 | require(!haltOnAnyFailure() || (haltOnAnyFailure() && haltOnFailureOfTypeExcept().isEmpty())) {
92 | "`haltOnAnyFailureExcept` should NOT be set when `haltOnAnyFailure` is set to true"
93 | }
94 | require(disjoint(runOnlySteps(), skipSteps())) {
95 | "`runOnlySteps` and `skipSteps` cannot contain same step names"
96 | }
97 | }
98 |
99 | companion object {
100 | @JvmStatic
101 | @SafeVarargs
102 | fun plus(vararg maps: Map): Map =
103 | maps.reduce { acc, map -> acc + map }
104 | }
105 | }
106 |
107 | fun interface CustomDynamicVariableGenerator {
108 | fun generate(variableName: String, currentStepReport: StepReport, rundown: Rundown): String
109 | }
110 |
111 | @Target(AnnotationTarget.CLASS)
112 | @Retention(AnnotationRetention.SOURCE)
113 | @Value.Style(
114 | typeImmutable = "*",
115 | typeAbstract = ["*Def"],
116 | builder = "configure",
117 | build = "off",
118 | depluralize = true,
119 | add = "*",
120 | put = "*",
121 | with = "override*",
122 | visibility = PUBLIC,
123 | )
124 | private annotation class Config
125 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/config/RequestConfig.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.config
9 |
10 | import com.salesforce.revoman.input.config.StepPick.PreTxnStepPick
11 | import com.squareup.moshi.JsonAdapter
12 | import io.vavr.control.Either
13 | import io.vavr.kotlin.left
14 | import io.vavr.kotlin.right
15 | import java.lang.reflect.Type
16 |
17 | data class RequestConfig
18 | internal constructor(
19 | val preTxnStepPick: PreTxnStepPick,
20 | val requestType: Type,
21 | val customTypeAdapter: Either, JsonAdapter.Factory>? = null,
22 | ) {
23 | companion object {
24 | @JvmStatic
25 | fun unmarshallRequest(preTxnStepPick: PreTxnStepPick, requestType: Type): RequestConfig =
26 | RequestConfig(preTxnStepPick, requestType)
27 |
28 | @JvmStatic
29 | fun unmarshallRequest(
30 | preTxnStepPick: PreTxnStepPick,
31 | requestType: Type,
32 | customTypeAdapter: JsonAdapter,
33 | ): RequestConfig = RequestConfig(preTxnStepPick, requestType, left(customTypeAdapter))
34 |
35 | @JvmStatic
36 | fun unmarshallRequest(
37 | preTxnStepPick: PreTxnStepPick,
38 | requestType: Type,
39 | customTypeAdapterFactory: JsonAdapter.Factory,
40 | ): RequestConfig = RequestConfig(preTxnStepPick, requestType, right(customTypeAdapterFactory))
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/config/ResponseConfig.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.config
9 |
10 | import com.salesforce.revoman.input.config.StepPick.PostTxnStepPick
11 | import com.squareup.moshi.JsonAdapter
12 | import io.vavr.control.Either
13 | import io.vavr.kotlin.left
14 | import io.vavr.kotlin.right
15 | import java.lang.reflect.Type
16 |
17 | data class ResponseConfig
18 | internal constructor(
19 | val postTxnStepPick: PostTxnStepPick,
20 | val ifSuccess: Boolean?,
21 | val responseType: Type,
22 | val customTypeAdapter: Either, JsonAdapter.Factory>? = null,
23 | ) {
24 | companion object {
25 | @JvmStatic
26 | fun unmarshallResponse(postTxnStepPick: PostTxnStepPick, successType: Type): ResponseConfig =
27 | ResponseConfig(postTxnStepPick, null, successType)
28 |
29 | @JvmStatic
30 | fun unmarshallResponse(
31 | postTxnStepPick: PostTxnStepPick,
32 | successType: Type,
33 | customTypeAdapter: JsonAdapter,
34 | ): ResponseConfig = ResponseConfig(postTxnStepPick, null, successType, left(customTypeAdapter))
35 |
36 | @JvmStatic
37 | fun unmarshallResponse(
38 | postTxnStepPick: PostTxnStepPick,
39 | successType: Type,
40 | customTypeAdapterFactory: JsonAdapter.Factory,
41 | ): ResponseConfig =
42 | ResponseConfig(postTxnStepPick, null, successType, right(customTypeAdapterFactory))
43 |
44 | @JvmStatic
45 | fun unmarshallSuccessResponse(
46 | postTxnStepPick: PostTxnStepPick,
47 | successType: Type,
48 | ): ResponseConfig = ResponseConfig(postTxnStepPick, true, successType)
49 |
50 | @JvmStatic
51 | fun unmarshallSuccessResponse(
52 | postTxnStepPick: PostTxnStepPick,
53 | successType: Type,
54 | customTypeAdapter: JsonAdapter,
55 | ): ResponseConfig = ResponseConfig(postTxnStepPick, true, successType, left(customTypeAdapter))
56 |
57 | @JvmStatic
58 | fun unmarshallSuccessResponse(
59 | postTxnStepPick: PostTxnStepPick,
60 | successType: Type,
61 | customTypeAdapterFactory: JsonAdapter.Factory,
62 | ): ResponseConfig =
63 | ResponseConfig(postTxnStepPick, true, successType, right(customTypeAdapterFactory))
64 |
65 | @JvmStatic
66 | fun unmarshallErrorResponse(
67 | postTxnStepPick: PostTxnStepPick,
68 | successType: Type,
69 | ): ResponseConfig = ResponseConfig(postTxnStepPick, false, successType)
70 |
71 | @JvmStatic
72 | fun unmarshallErrorResponse(
73 | postTxnStepPick: PostTxnStepPick,
74 | successType: Type,
75 | customTypeAdapter: JsonAdapter,
76 | ): ResponseConfig = ResponseConfig(postTxnStepPick, false, successType, left(customTypeAdapter))
77 |
78 | @JvmStatic
79 | fun unmarshallErrorResponse(
80 | postTxnStepPick: PostTxnStepPick,
81 | successType: Type,
82 | customTypeAdapterFactory: JsonAdapter.Factory,
83 | ): ResponseConfig =
84 | ResponseConfig(postTxnStepPick, false, successType, right(customTypeAdapterFactory))
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/FnTypes.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.json
9 |
10 | fun interface NestedNodeWriter {
11 | @Throws(Throwable::class) fun write(t: T)
12 | }
13 |
14 | fun interface NestedNodeReader {
15 | @Throws(Throwable::class) fun read(t: T, s: String)
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/JsonReaderUtils.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | @file:JvmName("JsonReaderUtils")
9 |
10 | package com.salesforce.revoman.input.json
11 |
12 | import com.squareup.moshi.JsonAdapter
13 | import com.squareup.moshi.JsonReader
14 | import com.squareup.moshi.Moshi
15 | import org.springframework.beans.BeanUtils
16 |
17 | fun nextString(reader: JsonReader): String = reader.nextString()
18 |
19 | fun nextBoolean(reader: JsonReader): Boolean = reader.nextBoolean()
20 |
21 | fun nextInt(reader: JsonReader): Int = reader.nextInt()
22 |
23 | fun nextLong(reader: JsonReader): Long = reader.nextLong()
24 |
25 | fun skipValue(reader: JsonReader) = reader.skipValue()
26 |
27 | fun nextName(reader: JsonReader): String = reader.nextName()
28 |
29 | fun objR(supplier: () -> T, reader: JsonReader, block: NestedNodeReader): T =
30 | with(reader) {
31 | beginObject()
32 | val item = supplier()
33 | while (hasNext()) {
34 | block.read(item, nextName())
35 | }
36 | endObject()
37 | item
38 | }
39 |
40 | fun JsonReader.objR(supplier: () -> T, block: T.(String) -> Unit): T =
41 | objR(supplier, this, block)
42 |
43 | fun listR(supplier: () -> T, reader: JsonReader, fn: NestedNodeReader): List? =
44 | reader.skipNullOr {
45 | val items = mutableListOf()
46 | beginArray()
47 | while (hasNext()) items += objR(supplier, this, fn)
48 | endArray()
49 | items
50 | }
51 |
52 | fun JsonReader.anyMapR(): Map? = skipNullOr {
53 | beginObject()
54 | val map = mutableMapOf()
55 | while (hasNext()) map += nextName() to readJsonValue()
56 | endObject()
57 | map
58 | }
59 |
60 | private fun JsonReader.skipNullOr(fn: JsonReader.() -> T): T? =
61 | if (peek() == JsonReader.Token.NULL) skipValue().let { null } else fn()
62 |
63 | fun JsonReader.readProps(pojoType: Class, bean: T, fieldName: String, moshi: Moshi) =
64 | skipNullOr {
65 | val propType: Class<*> = BeanUtils.findPropertyType(fieldName, pojoType)
66 | // * NOTE 15 Feb 2024 gopala.akshintala: Since data type info gets lost with JSON,
67 | // dynamicJsonAdapter cannot be used
68 | val delegate: JsonAdapter = moshi.adapter(propType)
69 | BeanUtils.getPropertyDescriptor(pojoType, fieldName)
70 | ?.writeMethod
71 | ?.invoke(bean, delegate.fromJson(this))
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/JsonWriterUtils.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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 | @file:JvmName("JsonWriterUtils")
9 |
10 | package com.salesforce.revoman.input.json
11 |
12 | import com.squareup.moshi.JsonAdapter
13 | import com.squareup.moshi.JsonWriter
14 | import org.springframework.beans.BeanUtils
15 |
16 | fun objW(name: String, obj: T?, writer: JsonWriter, fn: NestedNodeWriter): Unit =
17 | with(writer) {
18 | name(name)
19 | objW(obj, writer, fn)
20 | }
21 |
22 | fun JsonWriter.objW(name: String, obj: T?, fn: T.() -> Unit): Unit = objW(name, obj, this, fn)
23 |
24 | fun objW(obj: T?, writer: JsonWriter, fn: NestedNodeWriter): Unit =
25 | with(writer) {
26 | if (obj == null) {
27 | nullValue()
28 | } else {
29 | beginObject()
30 | fn.write(obj)
31 | endObject()
32 | }
33 | }
34 |
35 | fun JsonWriter.objW(obj: T?, fn: T.() -> Unit): Unit = objW(obj, this, fn)
36 |
37 | fun string(name: String, value: String?, writer: JsonWriter): Unit = writer.string(name, value)
38 |
39 | fun JsonWriter.string(name: String, value: String?) {
40 | name(name)
41 | value?.also(::value) ?: nullValue()
42 | }
43 |
44 | fun bool(name: String, value: Boolean?, writer: JsonWriter): Unit =
45 | with(writer) {
46 | name(name)
47 | value?.also(::value) ?: nullValue()
48 | }
49 |
50 | fun JsonWriter.bool(name: String, value: Boolean?) {
51 | name(name)
52 | value?.also(::value) ?: nullValue()
53 | }
54 |
55 | fun integer(name: String, value: Int?, writer: JsonWriter): Unit = writer.integer(name, value)
56 |
57 | fun JsonWriter.integer(name: String, value: Int?) {
58 | name(name)
59 | value?.also(::value) ?: nullValue()
60 | }
61 |
62 | fun doubl(name: String, value: Double?, writer: JsonWriter): Unit = writer.doubl(name, value)
63 |
64 | fun JsonWriter.doubl(name: String, value: Double?) {
65 | name(name)
66 | value?.also(::value) ?: nullValue()
67 | }
68 |
69 | fun lng(name: String, value: Long?, writer: JsonWriter): Unit = writer.lng(name, value)
70 |
71 | fun JsonWriter.lng(name: String, value: Long?) {
72 | name(name)
73 | value?.also(::value) ?: nullValue()
74 | }
75 |
76 | fun listW(
77 | name: String,
78 | list: List?,
79 | writer: JsonWriter,
80 | block: NestedNodeWriter,
81 | ): JsonWriter =
82 | with(writer) {
83 | name(name)
84 | listW(list, this, block)
85 | }
86 |
87 | fun listW(list: List?, writer: JsonWriter, fn: NestedNodeWriter): JsonWriter =
88 | with(writer) {
89 | when (list) {
90 | null -> nullValue()
91 | else -> {
92 | beginArray()
93 | list.forEach(fn::write)
94 | endArray()
95 | }
96 | }
97 | }
98 |
99 | fun JsonWriter.mapW(map: Map?, dynamicJsonAdapter: JsonAdapter) {
100 | map?.forEach { (key: String, value: Any?) ->
101 | name(key)
102 | dynamicJsonAdapter.toJson(this, value)
103 | } ?: nullValue()
104 | }
105 |
106 | // * NOTE 15 Mar 2025 gopala.akshintala: BeanUtils can read even the private fields with a getter
107 | fun JsonWriter.writeProps(
108 | pojoType: Class,
109 | bean: T,
110 | excludePropTypes: Set>,
111 | dynamicJsonAdapter: JsonAdapter,
112 | ) =
113 | BeanUtils.getPropertyDescriptors(pojoType)
114 | .asSequence()
115 | .filterNot {
116 | it.propertyType.name == "java.lang.Class" || excludePropTypes.contains(it.propertyType)
117 | }
118 | .forEach {
119 | checkNotNull(it.readMethod) {
120 | throw IllegalStateException("Please add a getter for the Property: `${it.name}`")
121 | }
122 | name(it.name)
123 | dynamicJsonAdapter.toJson(this, it.readMethod(bean))
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/adapters/salesforce/CompositeGraphRequest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.json.adapters.salesforce
9 |
10 | import com.squareup.moshi.JsonClass
11 |
12 | @JsonClass(generateAdapter = true)
13 | data class CompositeGraphRequest(val graphs: List) {
14 | @JsonClass(generateAdapter = true)
15 | data class Graph(val compositeRequest: List, val graphId: String) {
16 | @JsonClass(generateAdapter = true)
17 | data class CompositeRequest(
18 | val body: Map,
19 | val method: String,
20 | val referenceId: String,
21 | val url: String,
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/adapters/salesforce/CompositeGraphResponse.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.json.adapters.salesforce
9 |
10 | import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse.Graph.ErrorGraph
11 | import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse.Graph.ErrorGraph.ErrorGraphResponse.CompositeErrorResponse
12 | import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse.Graph.ErrorGraph.ErrorGraphResponse.CompositeErrorResponse.Body
13 | import com.salesforce.revoman.input.json.adapters.salesforce.CompositeGraphResponse.Graph.SuccessGraph
14 | import com.salesforce.revoman.input.json.factories.DiMorphicAdapter
15 | import com.squareup.moshi.Json
16 | import com.squareup.moshi.JsonClass
17 |
18 | @JsonClass(generateAdapter = true)
19 | data class CompositeGraphResponse(val graphs: List) {
20 | sealed interface Graph {
21 | val graphId: String
22 | val isSuccessful: Boolean
23 |
24 | @JsonClass(generateAdapter = true)
25 | data class SuccessGraph(
26 | override val graphId: String,
27 | val graphResponse: GraphResponse,
28 | override val isSuccessful: Boolean,
29 | ) : Graph {
30 | @JsonClass(generateAdapter = true)
31 | data class GraphResponse(val compositeResponse: List) {
32 | @JsonClass(generateAdapter = true)
33 | data class CompositeResponse(
34 | val body: Body,
35 | val httpHeaders: HttpHeaders,
36 | val httpStatusCode: Int,
37 | val referenceId: String,
38 | ) {
39 | @JsonClass(generateAdapter = true)
40 | data class Body(val errors: List, val id: String, val success: Boolean)
41 |
42 | @JsonClass(generateAdapter = true)
43 | data class HttpHeaders(@Json(name = "Location") val location: String)
44 | }
45 | }
46 | }
47 |
48 | @JsonClass(generateAdapter = true)
49 | data class ErrorGraph(
50 | override val graphId: String,
51 | val graphResponse: ErrorGraphResponse,
52 | override val isSuccessful: Boolean,
53 | ) : Graph {
54 | @Json(ignore = true)
55 | @get:JvmName("errorResponses")
56 | val errorResponses: List by lazy {
57 | graphResponse.compositeResponse.filter {
58 | it.httpStatusCode !in SUCCESSFUL_HTTP_STATUSES &&
59 | it.body.firstOrNull()?.let { error ->
60 | error.errorCode == PROCESSING_HALTED ||
61 | error.message == OPERATION_IN_TRANSACTION_FAILED_ERROR
62 | } != true
63 | }
64 | }
65 |
66 | @Json(ignore = true)
67 | @get:JvmName("firstErrorReferenceId")
68 | val firstErrorReferenceId: String? by lazy { errorResponses.firstOrNull()?.referenceId }
69 |
70 | @Json(ignore = true)
71 | @get:JvmName("firstErrorResponse")
72 | val firstErrorResponse: CompositeErrorResponse? by lazy { errorResponses.firstOrNull() }
73 |
74 | @Json(ignore = true)
75 | @get:JvmName("firstErrorResponseBody")
76 | val firstErrorResponseBody: Body? by lazy {
77 | errorResponses.firstOrNull()?.body?.firstOrNull()
78 | }
79 |
80 | @JsonClass(generateAdapter = true)
81 | data class ErrorGraphResponse(val compositeResponse: List) {
82 | @JsonClass(generateAdapter = true)
83 | data class CompositeErrorResponse(
84 | val body: List,
85 | val httpHeaders: HttpHeaders,
86 | val httpStatusCode: Int,
87 | val referenceId: String,
88 | ) {
89 | @JsonClass(generateAdapter = true)
90 | data class Body(val errorCode: String, val fields: List?, val message: String)
91 |
92 | @JsonClass(generateAdapter = true) class HttpHeaders
93 | }
94 | }
95 | }
96 | }
97 |
98 | companion object {
99 | @JvmField
100 | val ADAPTER =
101 | DiMorphicAdapter.of(
102 | Graph::class.java,
103 | "isSuccessful",
104 | { it.nextBoolean() },
105 | SuccessGraph::class.java,
106 | ErrorGraph::class.java,
107 | )
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/adapters/salesforce/Constants.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.json.adapters.salesforce
9 |
10 | internal val SUCCESSFUL_HTTP_STATUSES = 200..299
11 | internal const val PROCESSING_HALTED = "PROCESSING_HALTED"
12 | internal const val OPERATION_IN_TRANSACTION_FAILED_ERROR =
13 | "The transaction was rolled back since another operation in the same transaction failed."
14 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/input/json/factories/DiMorphicAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.input.json.factories
9 |
10 | import com.squareup.moshi.JsonAdapter
11 | import com.squareup.moshi.JsonDataException
12 | import com.squareup.moshi.JsonReader
13 | import com.squareup.moshi.JsonReader.Options
14 | import com.squareup.moshi.JsonWriter
15 | import com.squareup.moshi.Moshi
16 | import com.squareup.moshi.rawType
17 | import java.lang.reflect.Type
18 |
19 | class DiMorphicAdapter
20 | private constructor(
21 | private val labelKey: String,
22 | private val successAdapter: Triple<(JsonReader) -> Boolean, Type, JsonAdapter>,
23 | private val errorAdapter: Pair>,
24 | ) : JsonAdapter() {
25 | override fun fromJson(reader: JsonReader): Any? {
26 | val readerAtLabelKey = findLabelValue(reader.peekJson())
27 | val jsonAdapter =
28 | if (readerAtLabelKey.use(successAdapter.first)) successAdapter.third else errorAdapter.second
29 | return jsonAdapter.fromJson(reader)
30 | }
31 |
32 | private fun findLabelValue(reader: JsonReader): JsonReader {
33 | reader.beginObject()
34 | while (reader.hasNext()) {
35 | if (reader.selectName(Options.of(labelKey)) == -1) {
36 | reader.skipName()
37 | reader.skipValue()
38 | } else {
39 | return reader
40 | }
41 | }
42 | throw JsonDataException("Missing label for $labelKey")
43 | }
44 |
45 | override fun toJson(writer: JsonWriter, value: Any?) {
46 | val type: Class<*> = value!!.javaClass
47 | when (type) {
48 | successAdapter.second -> successAdapter.third.toJson(writer, value)
49 | errorAdapter.first -> errorAdapter.second.toJson(writer, value)
50 | }
51 | }
52 |
53 | companion object {
54 | @JvmStatic
55 | fun of(
56 | baseType: Type,
57 | labelKey: String,
58 | successPredicate: (JsonReader) -> Boolean,
59 | successType: Type,
60 | errorType: Type,
61 | ): Factory =
62 | object : Factory {
63 | override fun create(
64 | type: Type,
65 | annotations: Set,
66 | moshi: Moshi,
67 | ): JsonAdapter<*>? {
68 | if (type.rawType != baseType || annotations.isNotEmpty()) {
69 | return null
70 | }
71 | return DiMorphicAdapter(
72 | labelKey,
73 | Triple(successPredicate, successType, moshi.adapter(successType)),
74 | errorType to moshi.adapter(errorType),
75 | )
76 | .nullSafe()
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/HttpRequest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import arrow.core.Either
11 | import com.salesforce.revoman.internal.json.MoshiReVoman
12 | import com.salesforce.revoman.output.ExeType.HTTP_REQUEST
13 | import com.salesforce.revoman.output.report.Step
14 | import com.salesforce.revoman.output.report.TxnInfo
15 | import com.salesforce.revoman.output.report.failure.RequestFailure.HttpRequestFailure
16 | import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
17 | import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
18 | import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager
19 | import org.apache.hc.client5.http.socket.ConnectionSocketFactory
20 | import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory.INSTANCE
21 | import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory
22 | import org.apache.hc.core5.http.config.RegistryBuilder
23 | import org.apache.hc.core5.ssl.SSLContextBuilder
24 | import org.http4k.client.ApacheClient
25 | import org.http4k.core.HttpHandler
26 | import org.http4k.core.Request
27 | import org.http4k.core.Response
28 | import org.http4k.core.then
29 | import org.http4k.filter.DebuggingFilters
30 |
31 | @JvmSynthetic
32 | internal fun fireHttpRequest(
33 | currentStep: Step,
34 | httpRequest: Request,
35 | insecureHttp: Boolean,
36 | moshiReVoman: MoshiReVoman,
37 | ): Either> =
38 | runCatching(currentStep, HTTP_REQUEST) {
39 | // * NOTE gopala.akshintala 06/08/22: Preparing httpClient for each step,
40 | // * as there can be intermediate auths
41 | // ! TODO 29/01/24 gopala.akshintala: When would bearer token size be > 1?
42 | prepareHttpClient(insecureHttp)(httpRequest)
43 | }
44 | .mapLeft { HttpRequestFailure(it, TxnInfo(httpMsg = httpRequest, moshiReVoman = moshiReVoman)) }
45 | .map { TxnInfo(httpMsg = it, moshiReVoman = moshiReVoman) }
46 |
47 | private fun prepareHttpClient(insecureHttp: Boolean): HttpHandler =
48 | DebuggingFilters.PrintRequestAndResponse()
49 | .then(if (insecureHttp) ApacheClient(client = insecureApacheHttpClient()) else ApacheClient())
50 |
51 | /** Only for Testing. DO NOT USE IN PROD */
52 | private fun insecureApacheHttpClient(): CloseableHttpClient =
53 | SSLContextBuilder()
54 | .loadTrustMaterial(null) { _, _ -> true }
55 | .build()
56 | .run {
57 | HttpClientBuilder.create()
58 | .setConnectionManager(
59 | PoolingHttpClientConnectionManager(
60 | RegistryBuilder.create()
61 | .register("http", INSTANCE)
62 | .register("https", SSLConnectionSocketFactory(this) { _, _ -> true })
63 | .build()
64 | )
65 | )
66 | .build()
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/PmJsEval.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import arrow.core.Either
11 | import arrow.core.Either.Right
12 | import com.salesforce.revoman.internal.postman.PostmanSDK
13 | import com.salesforce.revoman.internal.postman.template.Item
14 | import com.salesforce.revoman.internal.postman.template.Request
15 | import com.salesforce.revoman.output.ExeType.POST_RES_JS
16 | import com.salesforce.revoman.output.ExeType.PRE_REQ_JS
17 | import com.salesforce.revoman.output.report.Step
18 | import com.salesforce.revoman.output.report.StepReport
19 | import com.salesforce.revoman.output.report.failure.RequestFailure.PreReqJSFailure
20 | import com.salesforce.revoman.output.report.failure.ResponseFailure.PostResJSFailure
21 |
22 | @JvmSynthetic
23 | internal fun executePreReqJS(
24 | currentStep: Step,
25 | itemWithRegex: Item,
26 | pm: PostmanSDK,
27 | ): Either {
28 | val preReqJS =
29 | itemWithRegex.event?.find { it.listen == "prerequest" }?.script?.exec?.joinToString("\n")
30 | return if (!preReqJS.isNullOrBlank()) {
31 | runCatching(currentStep, PRE_REQ_JS) {
32 | executePreReqJSWithPolyglot(preReqJS, itemWithRegex.request, pm)
33 | }
34 | .mapLeft { PreReqJSFailure(it, pm.currentStepReport.requestInfo!!.get()) }
35 | } else {
36 | Right(Unit)
37 | }
38 | }
39 |
40 | private fun executePreReqJSWithPolyglot(preReqJS: String, pmRequest: Request, pm: PostmanSDK) {
41 | pm.request = pm.from(pmRequest)
42 | pm.evaluateJS(preReqJS)
43 | }
44 |
45 | @JvmSynthetic
46 | internal fun executePostResJS(
47 | currentStep: Step,
48 | item: Item,
49 | pm: PostmanSDK,
50 | ): Either {
51 | val postResJs = item.event?.find { it.listen == "test" }?.script?.exec?.joinToString("\n")
52 | return if (!postResJs.isNullOrBlank()) {
53 | runCatching(currentStep, POST_RES_JS) {
54 | executePostResJSWithPolyglot(postResJs, item.request, pm.currentStepReport, pm)
55 | }
56 | .mapLeft {
57 | PostResJSFailure(
58 | it,
59 | pm.currentStepReport.requestInfo!!.get(),
60 | pm.currentStepReport.responseInfo!!.get(),
61 | )
62 | }
63 | } else {
64 | Right(Unit)
65 | }
66 | }
67 |
68 | private fun executePostResJSWithPolyglot(
69 | postResJS: String,
70 | pmRequest: Request,
71 | stepReport: StepReport,
72 | pm: PostmanSDK,
73 | ) {
74 | val httpResponse = stepReport.responseInfo!!.get().httpMsg
75 | pm.setRequestAndResponse(pm.from(pmRequest), httpResponse)
76 | pm.evaluateJS(postResJS, mapOf("responseBody" to httpResponse.bodyString()))
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/PostStepHook.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import com.salesforce.revoman.input.config.HookConfig
11 | import com.salesforce.revoman.input.config.HookConfig.StepHook.PostStepHook
12 | import com.salesforce.revoman.input.config.Kick
13 | import com.salesforce.revoman.input.config.StepPick.PostTxnStepPick
14 | import com.salesforce.revoman.internal.postman.PostmanSDK
15 | import com.salesforce.revoman.output.ExeType.POST_STEP_HOOK
16 | import com.salesforce.revoman.output.Rundown
17 | import com.salesforce.revoman.output.report.StepReport
18 | import com.salesforce.revoman.output.report.failure.HookFailure.PostStepHookFailure
19 | import io.github.oshai.kotlinlogging.KotlinLogging
20 |
21 | @JvmSynthetic
22 | internal fun postStepHookExe(kick: Kick, pm: PostmanSDK): PostStepHookFailure? =
23 | pickPostStepHooks(kick.postStepHooks(), pm.currentStepReport, pm.rundown)
24 | .map { postStepHook ->
25 | runCatching(pm.currentStepReport.step, POST_STEP_HOOK) {
26 | postStepHook.accept(pm.currentStepReport, pm.rundown)
27 | }
28 | .mapLeft {
29 | PostStepHookFailure(
30 | it,
31 | pm.currentStepReport.requestInfo!!.get(),
32 | pm.currentStepReport.responseInfo!!.get(),
33 | )
34 | }
35 | }
36 | .firstOrNull { it.isLeft() }
37 | ?.leftOrNull()
38 |
39 | private fun pickPostStepHooks(
40 | postStepHooks: List,
41 | currentStepReport: StepReport,
42 | rundown: Rundown,
43 | ): Sequence =
44 | postStepHooks
45 | .asSequence()
46 | .filter { (it.pick as PostTxnStepPick).pick(currentStepReport, rundown) }
47 | .map { it.stepHook as PostStepHook }
48 | .also {
49 | if (it.iterator().hasNext()) {
50 | logger.info { "${currentStepReport.step} Picked Post hook count : ${it.count()}" }
51 | currentStepReport.step.postStepHookCount = it.count()
52 | }
53 | }
54 |
55 | private val logger = KotlinLogging.logger {}
56 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/PreStepHook.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import com.salesforce.revoman.input.config.HookConfig
11 | import com.salesforce.revoman.input.config.HookConfig.StepHook.PreStepHook
12 | import com.salesforce.revoman.input.config.Kick
13 | import com.salesforce.revoman.input.config.StepPick.PreTxnStepPick
14 | import com.salesforce.revoman.internal.postman.PostmanSDK
15 | import com.salesforce.revoman.output.ExeType.PRE_STEP_HOOK
16 | import com.salesforce.revoman.output.Rundown
17 | import com.salesforce.revoman.output.report.Step
18 | import com.salesforce.revoman.output.report.TxnInfo
19 | import com.salesforce.revoman.output.report.failure.HookFailure.PreStepHookFailure
20 | import io.github.oshai.kotlinlogging.KotlinLogging
21 | import org.http4k.core.Request
22 |
23 | @JvmSynthetic
24 | internal fun preStepHookExe(
25 | currentStep: Step,
26 | kick: Kick,
27 | requestInfo: TxnInfo,
28 | pm: PostmanSDK,
29 | ): PreStepHookFailure? =
30 | pickPreStepHooks(kick.preStepHooks(), currentStep, requestInfo, pm.rundown)
31 | .map { preStepHook ->
32 | runCatching(currentStep, PRE_STEP_HOOK) {
33 | preStepHook.accept(currentStep, requestInfo, pm.rundown)
34 | }
35 | .mapLeft { PreStepHookFailure(it, requestInfo) }
36 | }
37 | .firstOrNull { it.isLeft() }
38 | ?.leftOrNull()
39 |
40 | @JvmSynthetic
41 | private fun pickPreStepHooks(
42 | preStepHooks: List,
43 | currentStep: Step,
44 | requestInfo: TxnInfo,
45 | rundown: Rundown,
46 | ): Sequence =
47 | preStepHooks
48 | .asSequence()
49 | .filter { (it.pick as PreTxnStepPick).pick(currentStep, requestInfo, rundown) }
50 | .map { it.stepHook as PreStepHook }
51 | .also {
52 | if (it.iterator().hasNext()) {
53 | logger.info { "$currentStep Picked Pre hook count : ${it.count()}" }
54 | currentStep.preStepHookCount = it.count()
55 | }
56 | }
57 |
58 | private val logger = KotlinLogging.logger {}
59 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/UnmarshallRequest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import arrow.core.Either
11 | import arrow.core.Either.Right
12 | import com.salesforce.revoman.input.config.Kick
13 | import com.salesforce.revoman.internal.json.MoshiReVoman
14 | import com.salesforce.revoman.internal.postman.PostmanSDK
15 | import com.salesforce.revoman.output.ExeType.UNMARSHALL_REQUEST
16 | import com.salesforce.revoman.output.report.Step
17 | import com.salesforce.revoman.output.report.TxnInfo
18 | import com.salesforce.revoman.output.report.failure.RequestFailure.UnmarshallRequestFailure
19 | import io.exoquery.pprint
20 | import io.github.oshai.kotlinlogging.KotlinLogging
21 | import java.lang.reflect.Type
22 | import org.http4k.core.ContentType.Companion.APPLICATION_JSON
23 | import org.http4k.core.Request
24 | import org.http4k.lens.contentType
25 |
26 | @JvmSynthetic
27 | internal fun unmarshallRequest(
28 | currentStep: Step,
29 | pmRequest: com.salesforce.revoman.internal.postman.template.Request,
30 | kick: Kick,
31 | moshiReVoman: MoshiReVoman,
32 | pm: PostmanSDK,
33 | ): Either> {
34 | val httpRequest = pmRequest.toHttpRequest(moshiReVoman)
35 | return when {
36 | httpRequest.bodyString().isNotBlank() &&
37 | APPLICATION_JSON.value.equals(httpRequest.contentType()?.value, true) -> {
38 | val requestType: Type =
39 | kick
40 | .requestConfig()
41 | .firstOrNull {
42 | it.preTxnStepPick.pick(
43 | currentStep,
44 | TxnInfo(httpMsg = httpRequest, moshiReVoman = moshiReVoman),
45 | pm.rundown,
46 | )
47 | }
48 | ?.also { logger.info { "$currentStep RequestConfig found : ${pprint(it)}" } }
49 | ?.requestType ?: Any::class.java
50 | runCatching(currentStep, UNMARSHALL_REQUEST) {
51 | pmRequest.body?.let { body -> moshiReVoman.fromJson(body.raw, requestType) }
52 | }
53 | .mapLeft {
54 | UnmarshallRequestFailure(
55 | it,
56 | TxnInfo(txnObjType = requestType, httpMsg = httpRequest, moshiReVoman = moshiReVoman),
57 | )
58 | }
59 | .map {
60 | TxnInfo(
61 | txnObjType = requestType,
62 | txnObj = it,
63 | httpMsg = httpRequest,
64 | moshiReVoman = moshiReVoman,
65 | )
66 | }
67 | }
68 | else -> {
69 | // ! TODO 15/10/23 gopala.akshintala: xml2Json
70 | logger.info {
71 | "$currentStep Blank Request body or content-type ${httpRequest.contentType()?.value} didn't match ${APPLICATION_JSON.value}"
72 | }
73 | Right(TxnInfo(isJson = false, httpMsg = httpRequest, moshiReVoman = moshiReVoman))
74 | }
75 | }
76 | }
77 |
78 | private val logger = KotlinLogging.logger {}
79 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/exe/UnmarshallResponse.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.exe
9 |
10 | import arrow.core.Either
11 | import arrow.core.Either.Right
12 | import com.salesforce.revoman.input.config.Kick
13 | import com.salesforce.revoman.internal.json.MoshiReVoman
14 | import com.salesforce.revoman.internal.postman.PostmanSDK
15 | import com.salesforce.revoman.output.ExeType.UNMARSHALL_RESPONSE
16 | import com.salesforce.revoman.output.report.TxnInfo
17 | import com.salesforce.revoman.output.report.failure.ResponseFailure.UnmarshallResponseFailure
18 | import io.exoquery.pprint
19 | import io.github.oshai.kotlinlogging.KotlinLogging
20 | import java.lang.reflect.Type
21 | import org.http4k.core.ContentType.Companion.APPLICATION_JSON
22 | import org.http4k.core.Response
23 | import org.http4k.lens.contentType
24 |
25 | @JvmSynthetic
26 | internal fun unmarshallResponse(
27 | kick: Kick,
28 | moshiReVoman: MoshiReVoman,
29 | pm: PostmanSDK,
30 | ): Either> {
31 | val httpResponse = pm.currentStepReport.responseInfo!!.get().httpMsg
32 | return when {
33 | httpResponse.bodyString().isNotBlank() &&
34 | APPLICATION_JSON.value.equals(httpResponse.contentType()?.value, true) -> {
35 | val httpStatus = httpResponse.status.successful
36 | val responseConfig =
37 | (kick.pickToResponseConfig()[httpStatus].orEmpty() +
38 | kick.pickToResponseConfig()[null].orEmpty())
39 | .let {
40 | it.firstOrNull { pick -> pick.postTxnStepPick.pick(pm.currentStepReport, pm.rundown) }
41 | }
42 | val currentStep = pm.currentStepReport.step
43 | val responseType: Type =
44 | responseConfig
45 | ?.also { logger.info { "$currentStep ResponseConfig found : ${pprint(it)}" } }
46 | ?.responseType ?: Any::class.java
47 | val requestInfo = pm.currentStepReport.requestInfo!!.get()
48 | runCatching(currentStep, UNMARSHALL_RESPONSE) {
49 | moshiReVoman.fromJson(httpResponse.bodyString(), responseType)
50 | }
51 | .mapLeft {
52 | UnmarshallResponseFailure(
53 | it,
54 | requestInfo,
55 | TxnInfo(txnObjType = responseType, httpMsg = httpResponse, moshiReVoman = moshiReVoman),
56 | )
57 | }
58 | .map {
59 | TxnInfo(
60 | txnObjType = responseType,
61 | txnObj = it,
62 | httpMsg = httpResponse,
63 | moshiReVoman = moshiReVoman,
64 | )
65 | }
66 | }
67 | else -> {
68 | logger.info {
69 | "${pm.currentStepReport.step} Blank Response body or ${httpResponse.contentType()?.value} didn't match ${APPLICATION_JSON.value}"
70 | }
71 | Right(TxnInfo(isJson = false, httpMsg = httpResponse, moshiReVoman = moshiReVoman))
72 | }
73 | }
74 | }
75 |
76 | private val logger = KotlinLogging.logger {}
77 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/adapters/BigDecimalAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.adapters
9 |
10 | import com.squareup.moshi.FromJson
11 | import com.squareup.moshi.ToJson
12 | import java.math.BigDecimal
13 |
14 | object BigDecimalAdapter {
15 | @ToJson fun toJson(value: BigDecimal) = value.toDouble()
16 |
17 | @FromJson fun fromJson(string: String) = BigDecimal(string)
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/adapters/EpochAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.adapters
9 |
10 | import com.squareup.moshi.FromJson
11 | import com.squareup.moshi.JsonAdapter
12 | import com.squareup.moshi.JsonReader
13 | import java.util.Date
14 | import kotlinx.datetime.Instant
15 | import kotlinx.datetime.toJavaInstant
16 |
17 | object EpochAdapter {
18 | @FromJson
19 | fun fromJson(reader: JsonReader, delegate: JsonAdapter): Date? {
20 | val epoch = reader.nextString()
21 | return if (epoch.matches("\\d+".toRegex())) {
22 | Date.from(Instant.fromEpochMilliseconds(epoch.toLong()).toJavaInstant())
23 | } else {
24 | delegate.fromJsonValue(epoch)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/adapters/TypeAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.adapters
9 |
10 | import com.squareup.moshi.FromJson
11 | import com.squareup.moshi.ToJson
12 | import java.lang.reflect.Type
13 |
14 | object TypeAdapter {
15 | @ToJson fun toJson(type: Type): String = type.toString()
16 |
17 | @FromJson fun fromJson(ignore: String): Type? = null
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/adapters/UUIDAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.adapters
9 |
10 | import com.squareup.moshi.FromJson
11 | import com.squareup.moshi.ToJson
12 | import java.util.UUID
13 |
14 | object UUIDAdapter {
15 | @ToJson fun toJson(uuid: UUID): String = uuid.toString()
16 |
17 | @FromJson fun fromJson(uuidStr: String): UUID = UUID.fromString(uuidStr)
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/factories/AlwaysSerializeNullsFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.factories
9 |
10 | import com.salesforce.revoman.internal.json.AlwaysSerializeNulls
11 | import com.squareup.moshi.JsonAdapter
12 | import com.squareup.moshi.Moshi
13 | import com.squareup.moshi.rawType
14 | import java.lang.reflect.Type
15 |
16 | internal class AlwaysSerializeNullsFactory : JsonAdapter.Factory {
17 | override fun create(type: Type, annotations: Set, moshi: Moshi): JsonAdapter<*>? {
18 | val rawType: Class<*> = type.rawType
19 | if (!rawType.isAnnotationPresent(AlwaysSerializeNulls::class.java)) {
20 | return null
21 | }
22 | val delegate: JsonAdapter = moshi.nextAdapter(this, type, annotations)
23 | return delegate.serializeNulls()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/factories/CaseInsensitiveEnumAdapter.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.factories
9 |
10 | import com.squareup.moshi.JsonAdapter
11 | import com.squareup.moshi.JsonDataException
12 | import com.squareup.moshi.JsonReader
13 | import com.squareup.moshi.JsonWriter
14 | import com.squareup.moshi.Moshi
15 | import com.squareup.moshi.internal.Util
16 | import com.squareup.moshi.rawType
17 | import java.lang.reflect.Type
18 |
19 | /**
20 | * ************************************************************************************************
21 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
22 | * Version 2.0 For full license text, see the LICENSE file in the repo root or
23 | * http://www.apache.org/licenses/LICENSE-2.0
24 | * ************************************************************************************************
25 | */
26 | /**
27 | * ************************************************************************************************
28 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
29 | * Version 2.0 For full license text, see the LICENSE file in the repo root or
30 | * http://www.apache.org/licenses/LICENSE-2.0
31 | * ************************************************************************************************
32 | */
33 | internal class CaseInsensitiveEnumAdapter>(private val enumType: Class) :
34 | JsonAdapter() {
35 | private val nameStrings =
36 | enumType.getEnumConstants().map { Util.jsonName(it.name, enumType.getField(it.name)) }
37 | private val options = JsonReader.Options.of(*nameStrings.toTypedArray())
38 |
39 | override fun fromJson(reader: JsonReader): T {
40 | val index = reader.selectString(options)
41 | return if (index != -1) {
42 | enumType.getEnumConstants()[index]
43 | } else if (reader.peek() != JsonReader.Token.STRING) {
44 | throw JsonDataException("Expected a string but was ${reader.peek()} at path ${reader.path}")
45 | } else {
46 | val value = reader.nextString()
47 | enumType.enumConstants.firstOrNull { it.name.compareTo(value, ignoreCase = true) == 0 }
48 | ?: throw JsonDataException(
49 | "Expected one of $nameStrings but was $value at path ${reader.path}"
50 | )
51 | }
52 | }
53 |
54 | override fun toJson(writer: JsonWriter, value: T?) {
55 | value?.also { writer.value(nameStrings[it.ordinal]) } ?: writer.nullValue()
56 | }
57 |
58 | companion object {
59 | @JvmField
60 | val FACTORY =
61 | object : Factory {
62 | override fun create(
63 | type: Type,
64 | annotations: Set,
65 | moshi: Moshi,
66 | ): JsonAdapter<*>? {
67 | val rawType: Class<*> = type.rawType
68 | if (!rawType.isEnum) {
69 | return null
70 | }
71 | return CaseInsensitiveEnumAdapter(rawType as Class>)
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/json/factories/IgnoreTypesFactory.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.json.factories
9 |
10 | import com.squareup.moshi.JsonAdapter
11 | import com.squareup.moshi.JsonAdapter.Factory
12 | import com.squareup.moshi.JsonReader
13 | import com.squareup.moshi.JsonWriter
14 | import com.squareup.moshi.Moshi
15 | import java.lang.reflect.Type
16 |
17 | internal class IgnoreTypesFactory(private val typesToIgnore: Set) : Factory {
18 | override fun create(type: Type, annotations: Set, moshi: Moshi): JsonAdapter<*>? {
19 | return if (typesToIgnore.contains(type)) {
20 | object : JsonAdapter() {
21 | override fun fromJson(reader: JsonReader): Type? {
22 | reader.skipValue()
23 | return null
24 | }
25 |
26 | override fun toJson(writer: JsonWriter, value: Type?) {
27 | writer.nullValue()
28 | }
29 | }
30 | } else null
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/postman/DynamicVariableGenerator.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.postman
9 |
10 | import io.github.serpro69.kfaker.faker
11 | import java.time.LocalDate
12 | import java.util.*
13 | import kotlin.random.Random.Default.nextBoolean
14 | import kotlin.random.Random.Default.nextInt
15 | import kotlin.random.Random.Default.nextLong
16 | import kotlinx.datetime.Clock
17 |
18 | private val faker = faker {}
19 |
20 | /**
21 | * @see Postman
23 | * Variables
24 | *
25 | * This may not be an exhaustive list of all dynamic variables supported by Postman. We keep
26 | * adding on the need-basis so it will grow over time. If what is need is not present here, You
27 | * may either contribute or use @see Custom Dynamic
29 | * Variables
30 | */
31 | private val dynamicVariableGenerators: Map String> =
32 | mapOf(
33 | // Common
34 | $$"$guid" to { UUID.randomUUID().toString() },
35 | $$"$timestamp" to { Clock.System.now().epochSeconds.toString() },
36 | $$"$isoTimestamp" to { Clock.System.now().toString() },
37 | $$"$randomUUID" to { UUID.randomUUID().toString() },
38 | // Text, numbers, and colors
39 | $$"$randomAlphaNumeric" to { randomAlphanumeric(1) },
40 | $$"$randomBoolean" to { nextBoolean().toString() },
41 | $$"$randomInt" to { nextInt(0, Int.MAX_VALUE).toString() },
42 | $$"$randomColor" to faker.color::name,
43 | $$"$randomHexColor" to { "#${getRandomHex()}${getRandomHex()}${getRandomHex()}" },
44 | // Internet and IP addresses
45 | $$"$randomIP" to faker.internet::iPv4Address,
46 | $$"$randomIPV6" to faker.internet::iPv6Address,
47 | $$"$randomMACAddress" to { faker.internet.macAddress() },
48 | $$"$randomPassword" to { randomAlphanumeric(15) },
49 | // Names
50 | $$"$randomFirstName" to faker.name::firstName,
51 | $$"$randomLastName" to faker.name::lastName,
52 | $$"$randomUserName" to { faker.name.firstName() + faker.name.lastName() },
53 | // Phone, address, and location
54 | $$"$randomCity" to faker.address::city,
55 | // Grammar
56 | $$"$randomAdjective" to faker.adjective::positive,
57 | $$"$randomWord" to faker.lorem::words,
58 | // Business
59 | $$"$randomCompanyName" to faker.company::name,
60 | $$"$randomProduct" to faker.industrySegments::sector,
61 | // Domains, emails, and usernames
62 | $$"$randomEmail" to { faker.internet.email() },
63 | // Date time
64 | $$"$currentDate" to { LocalDate.now().toString() },
65 | $$"$randomFutureDate" to
66 | {
67 | LocalDate.now().let { it.plusDays(nextLong(1, it.lengthOfYear().toLong())).toString() }
68 | },
69 | )
70 |
71 | fun getRandomHex() = nextInt(255).toString(16).uppercase()
72 |
73 | private val charPool = ('a'..'z') + ('A'..'Z') + ('0'..'9')
74 |
75 | fun randomAlphanumeric(length: Int) =
76 | (1..length).map { charPool[nextInt(0, charPool.size)] }.joinToString("")
77 |
78 | private val dynamicVariableGeneratorsWithPM: Map String> =
79 | mapOf($$"$currentRequestName" to { it.info.requestName })
80 |
81 | internal fun dynamicVariableGenerator(key: String, pm: PostmanSDK): String? =
82 | dynamicVariableGenerators[key]?.invoke() ?: dynamicVariableGeneratorsWithPM[key]?.invoke(pm)
83 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/postman/RegexReplacer.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.postman
9 |
10 | import com.salesforce.revoman.input.config.CustomDynamicVariableGenerator
11 | import com.salesforce.revoman.internal.postman.template.Auth.Bearer
12 | import com.salesforce.revoman.internal.postman.template.Item
13 | import com.salesforce.revoman.internal.postman.template.Request
14 |
15 | private const val VARIABLE_KEY = "variableKey"
16 | private val postManVariableRegex = "\\{\\{(?<$VARIABLE_KEY>[^{}]*?)}}".toRegex()
17 |
18 | class RegexReplacer(
19 | private val customDynamicVariableGenerators: Map =
20 | emptyMap(),
21 | private val dynamicVariableGenerator: (String, PostmanSDK) -> String? = ::dynamicVariableGenerator,
22 | ) {
23 | /**
24 | * ## Order of Variable resolution
25 | * - Custom Dynamic Variables
26 | * - Dynamic variables
27 | * - Environment built during execution
28 | * - Dynamic Environment supplied through config
29 | * - Postman Environment supplied as a file through config
30 | */
31 | internal fun replaceVariablesRecursively(stringWithRegex: String?, pm: PostmanSDK): String? =
32 | stringWithRegex?.let {
33 | postManVariableRegex.replace(it) { matchResult ->
34 | val variableKey = matchResult.groups[VARIABLE_KEY]?.value!!
35 | customDynamicVariableGenerators[variableKey]
36 | ?.let { cdvg ->
37 | replaceVariablesRecursively(
38 | cdvg.generate(variableKey, pm.currentStepReport, pm.rundown),
39 | pm,
40 | )
41 | }
42 | ?.also { value -> pm.environment[variableKey] = value }
43 | ?: replaceVariablesRecursively(dynamicVariableGenerator(variableKey, pm), pm)?.also {
44 | value ->
45 | pm.environment[variableKey] = value
46 | }
47 | ?: replaceVariablesRecursively(pm.getAsString(variableKey), pm)?.also { value ->
48 | pm.environment[variableKey] = value
49 | }
50 | ?: matchResult.value
51 | }
52 | }
53 |
54 | internal fun replaceVariablesInPmItem(item: Item, pm: PostmanSDK): Item =
55 | item.copy(request = replaceVariablesInRequestRecursively(item.request, pm))
56 |
57 | private fun replaceVariablesInBearer(bearer: Bearer?, pm: PostmanSDK): Bearer? =
58 | bearer?.copy(value = replaceVariablesRecursively(bearer.value, pm)!!)
59 |
60 | internal fun replaceVariablesInRequestRecursively(request: Request, pm: PostmanSDK): Request =
61 | request.copy(
62 | auth =
63 | request.auth?.copy(
64 | bearer = listOfNotNull(replaceVariablesInBearer(request.auth.bearer.firstOrNull(), pm))
65 | ),
66 | header =
67 | request.header.map { header ->
68 | header.copy(
69 | key = replaceVariablesRecursively(header.key, pm) ?: header.key,
70 | value = replaceVariablesRecursively(header.value, pm) ?: header.value,
71 | )
72 | },
73 | url =
74 | request.url.copy(raw = replaceVariablesRecursively(request.url.raw, pm) ?: request.url.raw),
75 | body =
76 | request.body?.copy(
77 | raw = replaceVariablesRecursively(request.body.raw, pm) ?: request.body.raw
78 | ),
79 | )
80 |
81 | internal fun replaceVariablesInEnv(pm: PostmanSDK): Map =
82 | pm.environment
83 | .toMap()
84 | .entries
85 | .associateBy(
86 | { replaceVariablesRecursively(it.key, pm)!! },
87 | {
88 | if (it.value is String?) replaceVariablesRecursively(it.value as String?, pm)
89 | else it.value
90 | },
91 | )
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/postman/template/Auth.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.postman.template
9 |
10 | import com.squareup.moshi.JsonClass
11 |
12 | @JsonClass(generateAdapter = true)
13 | data class Auth(val bearer: List, val type: String) {
14 | // ! TODO 09/01/24 gopala.akshintala: Support other ways to authorize
15 | @JsonClass(generateAdapter = true)
16 | data class Bearer(val key: String, val type: String, val value: String)
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/postman/template/Environment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.postman.template
9 |
10 | import com.salesforce.revoman.input.bufferFile
11 | import com.salesforce.revoman.input.bufferInputStream
12 | import com.salesforce.revoman.internal.json.AlwaysSerializeNulls
13 | import com.salesforce.revoman.internal.json.MoshiReVoman
14 | import com.squareup.moshi.JsonClass
15 | import com.squareup.moshi.Moshi
16 | import com.squareup.moshi.adapter
17 | import java.io.InputStream
18 | import kotlin.collections.plus
19 |
20 | @JsonClass(generateAdapter = true)
21 | internal data class Environment(val name: String?, val values: List) {
22 | @AlwaysSerializeNulls
23 | @JsonClass(generateAdapter = true)
24 | internal data class EnvValue(val key: String, val value: String?, val enabled: Boolean)
25 |
26 | companion object {
27 | const val POSTMAN_ENV_NAME = "env-from-revoman"
28 |
29 | fun fromMap(envMap: Map, moshiReVoman: MoshiReVoman): Environment {
30 | val values =
31 | envMap.entries.map { (key, value) -> EnvValue(key, moshiReVoman.anyToString(value), true) }
32 | return Environment(POSTMAN_ENV_NAME, values)
33 | }
34 |
35 | @OptIn(ExperimentalStdlibApi::class)
36 | internal fun mergeEnvs(
37 | pmEnvironmentPaths: Set,
38 | pmEnvironmentInputStreams: List,
39 | dynamicEnvironment: Map,
40 | ): Map {
41 | val envAdapter = Moshi.Builder().build().adapter()
42 | val envFromEnvFiles =
43 | (pmEnvironmentPaths.map { bufferFile(it) } +
44 | pmEnvironmentInputStreams.map { bufferInputStream(it) })
45 | .flatMap { envWithRegex ->
46 | envAdapter.fromJson(envWithRegex)?.values?.filter { it.enabled } ?: emptyList()
47 | }
48 | .associate { it.key to it.value }
49 | // * NOTE 10/09/23 gopala.akshintala: dynamicEnvironment keys replace envFromEnvFiles when
50 | // clashed
51 | // ! TODO 11 Jun 2025 gopala.akshintala: serialize only during regex replace
52 | return envFromEnvFiles + dynamicEnvironment
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/internal/postman/template/Template.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.internal.postman.template
9 |
10 | import com.salesforce.revoman.internal.json.MoshiReVoman
11 | import com.squareup.moshi.JsonClass
12 | import io.github.oshai.kotlinlogging.KotlinLogging
13 | import java.util.regex.Pattern
14 | import org.http4k.core.ContentType.Companion.APPLICATION_JSON
15 | import org.http4k.core.ContentType.Companion.Text
16 | import org.http4k.core.Method
17 | import org.http4k.core.Uri
18 | import org.http4k.core.queryParametersEncoded
19 | import org.http4k.core.with
20 | import org.http4k.lens.Header.CONTENT_TYPE
21 | import org.http4k.lens.bearerAuth
22 |
23 | @JsonClass(generateAdapter = true)
24 | internal data class Template(val item: List- , val auth: Auth?)
25 |
26 | @JsonClass(generateAdapter = true)
27 | data class Item(
28 | val name: String = "",
29 | val item: List
- ? = null,
30 | val request: Request = Request(),
31 | val event: List? = null,
32 | ) {
33 | @JvmField val httpMethod = request.method
34 | }
35 |
36 | @JsonClass(generateAdapter = true)
37 | data class Event(val listen: String, val script: Script) {
38 | @JsonClass(generateAdapter = true) data class Script(val exec: List)
39 | }
40 |
41 | @JsonClass(generateAdapter = true) data class Header(val key: String, val value: String)
42 |
43 | @JsonClass(generateAdapter = true) data class Url(val raw: String = "")
44 |
45 | @JsonClass(generateAdapter = true) data class Body(val mode: String, val raw: String)
46 |
47 | @JsonClass(generateAdapter = true)
48 | data class Request(
49 | @JvmField val auth: Auth? = null,
50 | @JvmField val method: String = "",
51 | @JvmField val header: List = emptyList(),
52 | @JvmField val url: Url = Url(),
53 | @JvmField val body: Body? = null,
54 | @JvmField val event: List? = null,
55 | ) {
56 | internal fun toHttpRequest(moshiReVoman: MoshiReVoman?): org.http4k.core.Request {
57 | val uri = Uri.of(url.raw.trim()).queryParametersEncoded()
58 | var contentTypeHeader =
59 | header.firstOrNull { CONTENT_TYPE.meta.name.equals(it.key, true) }?.value?.let { Text(it) }
60 | val cleansedRawBody =
61 | body?.raw?.trim()?.let {
62 | when {
63 | it.isBlank() -> it
64 | else ->
65 | // ! TODO 15 Mar 2025 gopala.akshintala: Detect the right content type if absent
66 | when {
67 | contentTypeHeader?.value == null ||
68 | (APPLICATION_JSON.value.equals(contentTypeHeader.value, true) &&
69 | containsComments(it)) -> {
70 | runCatching { moshiReVoman?.jsonToObjToPrettyJson(it, true) ?: it }
71 | .onSuccess {
72 | if (contentTypeHeader == null) {
73 | logger.info {
74 | "Detected JSON Content type, adding $APPLICATION_JSON as content-type Header"
75 | }
76 | contentTypeHeader = APPLICATION_JSON
77 | }
78 | }
79 | .getOrDefault(it)
80 | }
81 | else -> it
82 | }
83 | }
84 | } ?: ""
85 | val request =
86 | org.http4k.core
87 | .Request(Method.valueOf(method), uri)
88 | .headers(header.map { it.key.trim() to it.value.trim() })
89 | .body(cleansedRawBody)
90 | val requestWithAuth =
91 | auth?.bearer?.firstOrNull()?.value?.let {
92 | // * NOTE 11 May 2025 gopala.akshintala: Auth in headers will be overridden by Auth from
93 | // request, to align with Postman app behavior
94 | request.removeHeader("Authorization").bearerAuth(it)
95 | } ?: request
96 | return contentTypeHeader?.let { requestWithAuth.with(CONTENT_TYPE of it) } ?: requestWithAuth
97 | }
98 |
99 | companion object {
100 | private val COMMENT_PATTERN = Pattern.compile("//.*|/\\*[\\s\\S]*?\\*/")
101 |
102 | // * NOTE 24 Mar 2025 gopala.akshintala: This may give false positives when a string like
103 | // "https://..." is present in JSON key or value
104 | fun containsComments(str: String): Boolean =
105 | COMMENT_PATTERN.matcher(str).find().also {
106 | if (it) logger.info { "String may contain Comments" }
107 | }
108 | }
109 | }
110 |
111 | private val logger = KotlinLogging.logger {}
112 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/ExeType.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output
9 |
10 | enum class ExeType(private val exeName: String) {
11 | UNMARSHALL_REQUEST("unmarshall-request"),
12 | PRE_STEP_HOOK("pre-step-hook"),
13 | PRE_REQ_JS("pre-req-js"),
14 | HTTP_REQUEST("http-request"),
15 | HTTP_STATUS("http-status"),
16 | POST_RES_JS("post-res-js"),
17 | UNMARSHALL_RESPONSE("unmarshall-response"),
18 | POST_STEP_HOOK("post-step-hook");
19 |
20 | override fun toString(): String {
21 | return exeName
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/Rundown.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output
9 |
10 | import com.salesforce.revoman.input.config.StepPick.PostTxnStepPick
11 | import com.salesforce.revoman.output.postman.PostmanEnvironment
12 | import com.salesforce.revoman.output.report.Folder.Companion.FOLDER_DELIMITER
13 | import com.salesforce.revoman.output.report.StepReport
14 |
15 | data class Rundown(
16 | @JvmField val stepReports: List = emptyList(),
17 | @JvmField val mutableEnv: PostmanEnvironment,
18 | private val haltOnFailureOfTypeExcept: Map,
19 | @JvmField val providedStepsToExecuteCount: Int,
20 | ) {
21 | @get:JvmName("immutableEnv") val immutableEnv: Map by lazy { mutableEnv.toMap() }
22 |
23 | @get:JvmName("executedStepCount") val executedStepCount: Int by lazy { stepReports.size }
24 |
25 | @get:JvmName("httpFailureStepCount")
26 | val httpFailureStepCount: Int by lazy { stepReports.count { !it.isHttpStatusSuccessful } }
27 |
28 | @get:JvmName("unsuccessfulStepCount")
29 | val unsuccessfulStepCount: Int by lazy { stepReports.count { !it.isSuccessful } }
30 |
31 | @get:JvmName("executionFailureStepCount")
32 | val executionFailureStepCount: Int by lazy { stepReports.count { it.failure?.isLeft ?: false } }
33 |
34 | @get:JvmName("firstUnsuccessfulStepReport")
35 | val firstUnsuccessfulStepReport: StepReport? by lazy {
36 | stepReports.firstOrNull { !it.isSuccessful }
37 | }
38 |
39 | @get:JvmName("firstUnIgnoredUnsuccessfulStepReport")
40 | val firstUnIgnoredUnsuccessfulStepReport: StepReport? by lazy {
41 | stepReports.firstOrNull { stepReport ->
42 | !stepReport.isSuccessful && !isStepIgnoredForFailure(stepReport, this)
43 | }
44 | }
45 |
46 | @get:JvmName("areAllStepsSuccessful")
47 | val areAllStepsSuccessful: Boolean by lazy { stepReports.all { it.isSuccessful } }
48 |
49 | @get:JvmName("areAllStepsExceptIgnoredSuccessful")
50 | val areAllStepsExceptIgnoredSuccessful: Boolean by lazy {
51 | stepReports.all { it.isSuccessful || !isStepIgnoredForFailure(it, this) }
52 | }
53 |
54 | fun reportsForStepsInFolder(folderName: String): List =
55 | stepReports.filter { it.step.name.contains("$folderName$FOLDER_DELIMITER") }
56 |
57 | fun areAllStepsInFolderSuccessful(folderName: String): Boolean =
58 | reportsForStepsInFolder(folderName).all { it?.isSuccessful == true }
59 |
60 | fun reportForStepName(stepName: String): StepReport? =
61 | stepReports.firstOrNull { it.step.stepNameMatches(stepName) }
62 |
63 | fun filterReportExcludingStepsWithName(stepNames: Set): List =
64 | stepReports.filter { r -> !stepNames.any { r.step.stepNameMatches(it) } }
65 |
66 | fun filterReportIncludingStepsWithName(stepNames: Set): List =
67 | stepReports.filter { r -> stepNames.any { r.step.stepNameMatches(it) } }
68 |
69 | companion object {
70 | fun isStepIgnoredForFailure(stepReport: StepReport, rundown: Rundown): Boolean =
71 | rundown.haltOnFailureOfTypeExcept
72 | .asSequence()
73 | .map { (exeType, postTxnPick) ->
74 | stepReport.exeTypeForFailure == exeType &&
75 | (postTxnPick?.pick(stepReport, rundown) ?: false)
76 | }
77 | .any { it }
78 | }
79 | }
80 |
81 | fun List.endsWith(list: List): Boolean =
82 | list.isNotEmpty() && list.size < size && subList(lastIndex - list.lastIndex, size) == list
83 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/Step.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report
9 |
10 | import com.salesforce.revoman.internal.postman.template.Item
11 | import com.salesforce.revoman.output.endsWith
12 | import com.salesforce.revoman.output.report.Folder.Companion.FOLDER_DELIMITER
13 | import java.util.Collections.indexOfSubList
14 |
15 | data class Step(
16 | @JvmField val index: String,
17 | @JvmField val rawPMStep: Item,
18 | @JvmField val parentFolder: Folder? = null,
19 | ) {
20 | @JvmField val name: String = rawPMStep.name
21 | @JvmField var preStepHookCount: Int = 0
22 | @JvmField var postStepHookCount: Int = 0
23 | @JvmField
24 | val path = parentFolder?.let { "$it$STEP_NAME_SEPARATOR$name$STEP_NAME_TERMINATOR" } ?: name
25 | @JvmField
26 | val displayName = "$index$INDEX_SEPARATOR${rawPMStep.httpMethod}$HTTP_METHOD_SEPARATOR$path"
27 |
28 | @JvmField val isInRoot: Boolean = parentFolder == null
29 |
30 | fun isInFolder(folderPath: String): Boolean =
31 | parentFolder?.let {
32 | indexOfSubList(
33 | it.path.map { f -> f.name },
34 | folderPath.trim(*FOLDER_DELIMITER.toCharArray()).split(FOLDER_DELIMITER),
35 | ) != -1
36 | } ?: folderPath.isBlank()
37 |
38 | fun stepNameMatches(stepName: String): Boolean =
39 | name == stepName || displayName == stepName || pathEndsWith(stepName)
40 |
41 | private fun pathEndsWith(semiStepPath: String): Boolean {
42 | val stepNameFromPath: List = semiStepPath.split(STEP_NAME_SEPARATOR)
43 | if (stepNameFromPath.size != 2 || name != stepNameFromPath[1]) {
44 | return false
45 | }
46 | val folderPath = stepNameFromPath[0].split(FOLDER_DELIMITER)
47 | return parentFolder?.path?.endsWith(folderPath) ?: folderPath.isEmpty()
48 | }
49 |
50 | override fun toString(): String = displayName
51 |
52 | companion object {
53 | const val HTTP_METHOD_SEPARATOR = " ~~> "
54 | const val INDEX_SEPARATOR = " ### "
55 | const val STEP_NAME_SEPARATOR = "<|||"
56 | const val STEP_NAME_TERMINATOR = "|||>"
57 | }
58 | }
59 |
60 | data class Folder
61 | @JvmOverloads
62 | constructor(
63 | @JvmField val name: String,
64 | @JvmField val parent: Folder? = null,
65 | @JvmField val subFolders: MutableList = mutableListOf(),
66 | ) {
67 | @JvmField val isRoot: Boolean = parent == null
68 | @JvmField val parentPath: List = parent?.parentPath?.plus(parent) ?: emptyList()
69 | @JvmField val path: List = parentPath + this
70 |
71 | override fun toString(): String =
72 | when {
73 | isRoot -> name
74 | else ->
75 | parentPath.joinToString(separator = FOLDER_DELIMITER, postfix = FOLDER_DELIMITER) {
76 | it.name
77 | } + name
78 | }
79 |
80 | companion object {
81 | const val FOLDER_DELIMITER = "|>"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/TxnInfo.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report
9 |
10 | import com.salesforce.revoman.internal.json.MoshiReVoman
11 | import com.squareup.moshi.JsonAdapter
12 | import com.squareup.moshi.rawType
13 | import io.vavr.control.Either
14 | import java.lang.reflect.Type
15 | import java.util.Collections.indexOfSubList
16 | import org.http4k.core.HttpMessage
17 | import org.http4k.core.Request
18 | import org.http4k.core.Response
19 |
20 | data class TxnInfo
21 | @JvmOverloads
22 | constructor(
23 | @JvmField val isJson: Boolean = true,
24 | @JvmField val txnObjType: Type? = if (isJson) Any::class.java else null,
25 | @JvmField val txnObj: Any? = null,
26 | @JvmField val httpMsg: HttpMsgT,
27 | val moshiReVoman: MoshiReVoman,
28 | ) {
29 | @JvmOverloads
30 | fun getTypedTxnObj(
31 | targetType: Type = this.txnObjType ?: Any::class.java,
32 | customAdapters: List = emptyList(),
33 | customAdaptersWithType: Map, JsonAdapter.Factory>> =
34 | emptyMap(),
35 | typesToIgnore: Set = emptySet(),
36 | ): PojoT? =
37 | when {
38 | // ! TODO 15/10/23 gopala.akshintala: xml2Json
39 | txnObj == null -> null
40 | targetType.rawType.isInstance(txnObj) -> txnObj
41 | targetType == String::class.java -> txnObj
42 | !isJson ->
43 | throw IllegalCallerException("Non JSON (like XML) marshalling to POJO is not yet supported")
44 | else -> {
45 | moshiReVoman.addAdapters(customAdapters, customAdaptersWithType, typesToIgnore)
46 | moshiReVoman.fromJson(httpMsg.bodyString(), targetType)
47 | }
48 | }
49 | as PojoT?
50 |
51 | @JvmOverloads
52 | inline fun getTxnObj(
53 | customAdapters: List = emptyList(),
54 | customAdaptersWithType: Map, JsonAdapter.Factory>> =
55 | emptyMap(),
56 | typesToIgnore: Set = emptySet(),
57 | ): PojoT? =
58 | when {
59 | txnObj == null -> null
60 | txnObj is PojoT -> txnObj
61 | PojoT::class == String::class -> txnObj
62 | !isJson ->
63 | throw IllegalCallerException("Non JSON (like XML) marshalling to POJO is not yet supported")
64 | else -> {
65 | moshiReVoman.addAdapters(customAdapters, customAdaptersWithType, typesToIgnore)
66 | moshiReVoman.fromJson(httpMsg.bodyString())
67 | }
68 | }
69 | as PojoT?
70 |
71 | fun containsHeader(key: String): Boolean = httpMsg.headers.toMap().containsKey(key)
72 |
73 | fun containsHeader(key: String, value: String): Boolean = httpMsg.headers.contains(key to value)
74 |
75 | fun getHeaderValue(key: String): String? = httpMsg.header(key)
76 |
77 | override fun toString(): String {
78 | val prefix =
79 | when (httpMsg) {
80 | is Request -> "⬆️ Request Info ~~>"
81 | is Response -> "⬇️ Response Info <~~"
82 | else -> "TxnInfo"
83 | }
84 | return "\n$prefix\nType=$txnObjType\nObj=$txnObj\n$httpMsg"
85 | }
86 |
87 | companion object {
88 | @JvmStatic fun TxnInfo.getURIPath(): String = httpMsg.uri.path
89 |
90 | @JvmStatic
91 | fun TxnInfo.uriPathContains(path: String): Boolean =
92 | indexOfSubList(httpMsg.uri.path.trim('/').split("/"), path.trim('/').split("/")) != -1
93 |
94 | @JvmStatic
95 | fun TxnInfo.uriPathEndsWith(path: String): Boolean {
96 | val sourcePath = httpMsg.uri.path.trim('/').split("/")
97 | val targetPath = path.trim('/').split("/")
98 | val indexOfSubList = indexOfSubList(sourcePath, targetPath)
99 | return indexOfSubList != -1 && indexOfSubList + targetPath.lastIndex == sourcePath.lastIndex
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/failure/ExeFailure.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report.failure
9 |
10 | import com.salesforce.revoman.output.ExeType
11 |
12 | sealed class ExeFailure {
13 | abstract val exeType: ExeType
14 | abstract val failure: Throwable
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/failure/HookFailure.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report.failure
9 |
10 | import com.salesforce.revoman.output.ExeType.POST_STEP_HOOK
11 | import com.salesforce.revoman.output.ExeType.PRE_STEP_HOOK
12 | import com.salesforce.revoman.output.report.TxnInfo
13 | import org.http4k.core.Request
14 | import org.http4k.core.Response
15 |
16 | sealed class HookFailure : ExeFailure() {
17 |
18 | data class PreStepHookFailure(
19 | override val failure: Throwable,
20 | @JvmField val requestInfo: TxnInfo,
21 | ) : HookFailure() {
22 | override val exeType = PRE_STEP_HOOK
23 | }
24 |
25 | data class PostStepHookFailure(
26 | override val failure: Throwable,
27 | @JvmField val requestInfo: TxnInfo,
28 | @JvmField val responseInfo: TxnInfo,
29 | ) : HookFailure() {
30 | override val exeType = POST_STEP_HOOK
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/failure/HttpStatusUnsuccessful.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report.failure
9 |
10 | import com.salesforce.revoman.output.ExeType.HTTP_STATUS
11 | import com.salesforce.revoman.output.report.TxnInfo
12 | import org.http4k.core.Request
13 | import org.http4k.core.Response
14 |
15 | data class HttpStatusUnsuccessful(
16 | @JvmField val requestInfo: TxnInfo,
17 | @JvmField val responseInfo: TxnInfo,
18 | ) {
19 | @JvmField val exeType = HTTP_STATUS
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/failure/RequestFailure.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report.failure
9 |
10 | import com.salesforce.revoman.output.ExeType.HTTP_REQUEST
11 | import com.salesforce.revoman.output.ExeType.PRE_REQ_JS
12 | import com.salesforce.revoman.output.ExeType.UNMARSHALL_REQUEST
13 | import com.salesforce.revoman.output.report.TxnInfo
14 | import org.http4k.core.Request
15 |
16 | sealed class RequestFailure : ExeFailure() {
17 | abstract val requestInfo: TxnInfo
18 |
19 | data class PreReqJSFailure(
20 | override val failure: Throwable,
21 | override val requestInfo: TxnInfo,
22 | ) : RequestFailure() {
23 | override val exeType = PRE_REQ_JS
24 | }
25 |
26 | data class UnmarshallRequestFailure(
27 | override val failure: Throwable,
28 | override val requestInfo: TxnInfo,
29 | ) : RequestFailure() {
30 | override val exeType = UNMARSHALL_REQUEST
31 | }
32 |
33 | data class HttpRequestFailure(
34 | override val failure: Throwable,
35 | override val requestInfo: TxnInfo,
36 | ) : RequestFailure() {
37 | override val exeType = HTTP_REQUEST
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/salesforce/revoman/output/report/failure/ResponseFailure.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * ************************************************************************************************
3 | * Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
4 | * Version 2.0 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.output.report.failure
9 |
10 | import com.salesforce.revoman.output.ExeType.POST_RES_JS
11 | import com.salesforce.revoman.output.ExeType.UNMARSHALL_RESPONSE
12 | import com.salesforce.revoman.output.report.TxnInfo
13 | import org.http4k.core.Request
14 | import org.http4k.core.Response
15 |
16 | sealed class ResponseFailure : ExeFailure() {
17 | abstract val requestInfo: TxnInfo
18 | abstract val responseInfo: TxnInfo
19 |
20 | data class PostResJSFailure(
21 | override val failure: Throwable,
22 | override val requestInfo: TxnInfo,
23 | override val responseInfo: TxnInfo,
24 | ) : ResponseFailure() {
25 | override val exeType = POST_RES_JS
26 | }
27 |
28 | data class UnmarshallResponseFailure(
29 | override val failure: Throwable,
30 | override val requestInfo: TxnInfo,
31 | override val responseInfo: TxnInfo,
32 | ) : ResponseFailure() {
33 | override val exeType = UNMARSHALL_RESPONSE
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/test/java/com/salesforce/revoman/input/json/adapters/SObjectGraphRequestMarshaller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 salesforce.com, inc.
3 | * All Rights Reserved
4 | * Company Confidential
5 | */
6 |
7 | package com.salesforce.revoman.input.json.adapters;
8 |
9 | import static com.salesforce.revoman.input.json.JsonWriterUtils.listW;
10 | import static com.salesforce.revoman.input.json.JsonWriterUtils.mapW;
11 | import static com.salesforce.revoman.input.json.JsonWriterUtils.objW;
12 | import static com.salesforce.revoman.input.json.JsonWriterUtils.string;
13 | import static java.util.Collections.emptySet;
14 |
15 | import com.salesforce.revoman.input.json.pojo.SObjectGraphRequest;
16 | import com.squareup.moshi.FromJson;
17 | import com.squareup.moshi.JsonAdapter;
18 | import com.squareup.moshi.JsonReader;
19 | import com.squareup.moshi.JsonWriter;
20 | import com.squareup.moshi.ToJson;
21 | import java.util.Map;
22 | import java.util.Set;
23 | import kotlin.collections.CollectionsKt;
24 | import kotlin.collections.MapsKt;
25 |
26 | /** A light-weight Marshaller/Deserializer to convert SObjectGraphRequest --> JSON */
27 | public class SObjectGraphRequestMarshaller {
28 |
29 | static final String MASK = "******";
30 | private final Map paramsToWrite;
31 | private final Set fieldNamesToMaskValues;
32 |
33 | private SObjectGraphRequestMarshaller(
34 | Map paramsToWrite, Set fieldNamesToMaskValues) {
35 | this.paramsToWrite = paramsToWrite;
36 | this.fieldNamesToMaskValues = fieldNamesToMaskValues;
37 | }
38 |
39 | /**
40 | * @param paramsToWrite Any extra params to insert inside the JSON, at the same level and outside
41 | * `graph`
42 | */
43 | public static SObjectGraphRequestMarshaller adapter(Map paramsToWrite) {
44 | return new SObjectGraphRequestMarshaller(paramsToWrite, emptySet());
45 | }
46 |
47 | public static SObjectGraphRequestMarshaller adapter(
48 | Map paramsToWrite, Set fieldNamesToMaskValues) {
49 | return new SObjectGraphRequestMarshaller(paramsToWrite, fieldNamesToMaskValues);
50 | }
51 |
52 | /** Marshals SObjectGraphRequest to PQ payload */
53 | @SuppressWarnings("unused")
54 | @ToJson
55 | public void toJson(
56 | JsonWriter writer,
57 | SObjectGraphRequest sObjectGraphRequest,
58 | JsonAdapter