Skip to content

Commit

Permalink
Release 0.5.6
Browse files Browse the repository at this point in the history
Added Generics to some APIs

Signed-off-by: Gopal S Akshintala <[email protected]>
  • Loading branch information
overfullstack committed Feb 2, 2025
1 parent bdd8bd1 commit b095a91
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 48 deletions.
45 changes: 30 additions & 15 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ endif::[]
:pmtemplates: src/integrationTest/resources/pm-templates
:imagesdir: docs/images
:prewrap!:
:revoman-version: 0.5.5
:revoman-version: 0.5.6

'''

Expand Down Expand Up @@ -127,7 +127,7 @@ In the case of collision between variable keys, the precedence order to derive a
You can _kick off_ this *Template-Driven Testing* by invoking `ReVoman.revUp()`,
supplying your Postman templates and environments, and all your customizations through a configuration:

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
----
final var rundown =
ReVoman.revUp(
Expand All @@ -150,7 +150,7 @@ by supplying the template and environment path:

ifdef::env-github[]

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
.link:{integrationtestdir}/com/salesforce/revoman/integration/restfulapidev/RestfulAPIDevTest.java[RestfulAPIDevTest.java, tag=revoman-simple-demo]
----
@Test
Expand All @@ -162,26 +162,29 @@ void restfulApiDev() {
.templatePath(PM_COLLECTION_PATH) // <2>
.environmentPath(PM_ENVIRONMENT_PATH) // <3>
.off());
assertThat(rundown.stepReports).hasSize(3); // <4>
assertThat(rundown.firstUnIgnoredUnsuccessfulStepReport()).isNull(); // <4>
assertThat(rundown.stepReports).hasSize(3); // <5>
}
----
<1> `revUp` is the method to call passing a configuration, built as below
<2> Supply an exported Postman collection JSON file path
<3> Supply an exported Postman environment JSON file path
<4> Run more assertions on the <<Rundown>>
<4> Assert that the execution doesn't have any failures
<5> Run more assertions on the <<Rundown>>

endif::[]
ifndef::env-github[]

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
.link:{integrationtestdir}/com/salesforce/revoman/integration/restfulapidev/RestfulAPIDevTest.java[RestfulAPIDevTest.java,tag=revoman-simple-demo]
----
include::{integrationtestdir}/com/salesforce/revoman/integration/restfulapidev/RestfulAPIDevTest.java[tag=revoman-simple-demo]
----
<1> `revUp` is the method to call passing a configuration, built as below
<2> Supply an exported Postman collection JSON file path
<3> Supply an exported Postman environment JSON file path
<4> Run more assertions on the <<Rundown>>
<4> Assert that the execution doesn't have any failures
<5> Run more assertions on the <<Rundown>>

endif::[]

Expand All @@ -191,7 +194,7 @@ After all this, you receive back a detailed *Rundown* in return.
It contains everything you need to know about what happened in an execution,
such that you can seamlessly run more assertions on top of the run.

[source,kotlin,indent=0,options="nowrap"]
[source,kotlin,indent=0,tabsize=2,options="nowrap"]
----
Rundown(
val stepReports: List<StepReport>,
Expand Down Expand Up @@ -222,7 +225,7 @@ you can add more _bells and whistles_ 🔔:

ifdef::env-github[]

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
.link:{integrationtestdir}/com/salesforce/revoman/integration/core/pq/PQE2EWithSMTest.java[PQE2EWithSMTest.java, tag=pq-e2e-with-revoman-config-demo]
----
final var pqRundown =
Expand Down Expand Up @@ -313,7 +316,7 @@ assertThat(pqRundown.mutableEnv)
endif::[]
ifndef::env-github[]

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
.link:{integrationtestdir}/com/salesforce/revoman/integration/core/pq/PQE2EWithSMTest.java[PQE2ETest.java, tag=pq-e2e-with-revoman-config-demo]
----
include::{integrationtestdir}/com/salesforce/revoman/integration/core/pq/PQE2EWithSMTest.java[tag=pq-e2e-with-revoman-config-demo]
Expand Down Expand Up @@ -378,8 +381,20 @@ image:pq-exe-logging.gif[Monitor Execution]
* There may be a POJO that inherits or contains legacy types that are hard or impossible to serialize. ReṼoman lets you serialize only types that matter, through `globalSkipTypes`, where you can filter out these legacy types from Marshalling/Unmarshalling
* The JSON structure may not align with the POJO, and you may need a _Custom Type Adapter_ for Marshalling/Unmarshalling. Moshi has it covered for you with its advanced adapter mechanism and ReṼoman accepts Moshi adapters via:
** `requestConfig` — For types present as part of request payload for qualified Steps
** `responseConfig` — For types present as part of response payload for qualified Steps
** `globalCustomTypeAdapters` — For types present as part of request payload anywhere
** `responseConfig` — For types present as part of response payload for qualified Steps. You can configure separate adapters for success and error response. Use bundled static factory methods like `unmarshallSuccessResponse()` and `unmarshallErrorResponse()` for expressiveness.

[TIP]
====
Success or Error response is determined by default with HTTP Status Code (SUCCESSFUL = `200 <=statusCode <=299`). There may be a scenario
that you cannot depend on HTTP Status Code to distinguish between Success or Error.
In such a case,
you can leverage a bundled Moshi factory link:{sourcedir}/com/salesforce/revoman/input/json/factories/DiMorphicAdapter.kt[DiMorphicAdapter]
that deals with it dynamically.
Refer link:{sourcedir}/com/salesforce/revoman/input/json/adapters/CompositeGraphResponse.kt[CompositeGraphResponse]
for an example usage
====

** `globalCustomTypeAdapters` — For types present as part of request/response payload anywhere
* ReṼoman also comes bundled with link:{sourcedir}/com/salesforce/revoman/input/json/JsonReaderUtils.kt[JSON Reader utils] and link:{sourcedir}/com/salesforce/revoman/input/json/JsonWriterUtils.kt[JSON Writer utils] to help build Moshi adapters.

TIP: Refer link:{integrationtestdir}/com/salesforce/revoman/integration/core/pq/adapters/ConnectInputRepWithGraphAdapter.java[ConnectInputRepWithGraphAdapter]
Expand Down Expand Up @@ -418,15 +433,15 @@ ReṼoman comes
bundled with some predicates under the namespace `PreTxnStepPick.PickUtils`/`PostTxnStepPick.PickUtils` e.g `beforeStepContainingURIPathOfAny`,
`afterStepName` etc. If those don't fit your needs, you can write your own custom predicates like below:

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
----
final var preTxnStepPick = (currentStep, requestInfo, rundown) -> LOGGER.info("Picked `preLogHook` before stepName: {}", currentStep)
final var postTxnStepPick = (stepReport, rundown) -> LOGGER.info("Picked `postLogHook` after stepName: {}", stepReport.step.displayName)
----

Add them to the config as below:

[source,java,indent=0,options="nowrap"]
[source,java,indent=0,tabsize=2,options="nowrap"]
----
.hooks(
pre(
Expand Down Expand Up @@ -481,7 +496,7 @@ npm install moment
----

.Use inside pre-req and post-res scripts
[source,javascript,indent=0,options="nowrap"]
[source,javascript,indent=0,tabsize=2,options="nowrap"]
----
var moment = require("moment")
var _ = require('lodash')
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
* ************************************************************************************************
*/
const val GROUP_ID = "com.salesforce.revoman"
const val VERSION = "0.5.5"
const val VERSION = "0.5.6"
const val ARTIFACT_ID = "revoman"
const val STAGING_PROFILE_ID = "1ea0a23e61ba7d"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* ************************************************************************************************
* Copyright (c) 2023, Salesforce, Inc. All rights reserved. SPDX-License-Identifier: Apache License
* Version 2.0 For full license text, see the LICENSE file in the repo root or
* http://www.apache.org/licenses/LICENSE-2.0
* ************************************************************************************************
*/
package com.salesforce.revoman.integration.pokemon

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class AllPokemon(
@Json(name = "count") val count: Int,
@Json(name = "next") val next: String,
@Json(name = "previous") val previous: Any?,
@Json(name = "results") val results: List<Result>,
) {
@JsonClass(generateAdapter = true)
data class Result(@Json(name = "name") val name: String, @Json(name = "url") val url: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.salesforce.revoman.input.config.HookConfig.post;
import static com.salesforce.revoman.input.config.HookConfig.pre;
import static com.salesforce.revoman.input.config.ResponseConfig.unmarshallResponse;
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepContainingHeader;
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepContainingURIPathOfAny;
import static com.salesforce.revoman.input.config.StepPick.PostTxnStepPick.afterStepName;
Expand Down Expand Up @@ -89,17 +90,19 @@ public void accept(
Mockito.spy(
new PostStepHook() {
@Override
public void accept(@NotNull StepReport ignore2, @NotNull Rundown rundown) {
public void accept(@NotNull StepReport stepReport, @NotNull Rundown rundown) {
assertThat(rundown.mutableEnv).containsEntry("limit", String.valueOf(newLimit));
assertThat(rundown.mutableEnv).containsEntry("pokemonName", "bulbasaur");
final var results =
stepReport.responseInfo.get().<AllPokemon>getTypedTxnObj().getResults();
assertThat(results.size()).isEqualTo(newLimit);
}
});
//noinspection Convert2Lambda
final var postHookAfterURIPath =
Mockito.spy(
new PostStepHook() {
@Override
public void accept(@NotNull StepReport stepReport, @NotNull Rundown ignore) {
public void accept(@NotNull StepReport stepReport, @NotNull Rundown rundown) {
LOGGER.info(
"Picked `postHookAfterURIPath` after stepName: {} with raw URI: {}",
stepReport.step.displayName,
Expand All @@ -111,6 +114,7 @@ public void accept(@NotNull StepReport stepReport, @NotNull Rundown ignore) {
Kick.configure()
.templatePath(PM_COLLECTION_PATH)
.environmentPath(PM_ENVIRONMENT_PATH)
.responseConfig(unmarshallResponse(afterStepName("all-pokemon"), AllPokemon.class))
.hooks(
pre(beforeStepName("all-pokemon"), preHook),
post(afterStepName("all-pokemon"), postHook),
Expand All @@ -121,11 +125,7 @@ public void accept(@NotNull StepReport stepReport, @NotNull Rundown ignore) {
.haltOnAnyFailure(true)
.off());

Mockito.verify(preHook, times(1)).accept(any(), any(), any());
Mockito.verify(postHook, times(1)).accept(any(), any());
Mockito.verify(postHookAfterURIPath, times(1)).accept(any(), any());
Mockito.verify(preLogHook, times(1)).accept(any(), any(), any());
Mockito.verify(postLogHook, times(1)).accept(any(), any());
assertThat(pokeRundown.firstUnIgnoredUnsuccessfulStepReport()).isNull();
assertThat(pokeRundown.stepReports).hasSize(5);
assertThat(pokeRundown.mutableEnv)
.containsExactlyEntriesIn(
Expand All @@ -139,5 +139,10 @@ public void accept(@NotNull StepReport stepReport, @NotNull Rundown ignore) {
"gender", "female",
"ability", "stench",
"nature", "hardy"));
Mockito.verify(preHook, times(1)).accept(any(), any(), any());
Mockito.verify(postHook, times(1)).accept(any(), any());
Mockito.verify(postHookAfterURIPath, times(1)).accept(any(), any());
Mockito.verify(preLogHook, times(1)).accept(any(), any(), any());
Mockito.verify(postLogHook, times(1)).accept(any(), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ void restfulApiDev() {
.environmentPath(PM_ENVIRONMENT_PATH) // <3>
.nodeModulesRelativePath("js")
.off());
assertThat(rundown.stepReports).hasSize(3); // <4>
assertThat(rundown.firstUnIgnoredUnsuccessfulStepReport()).isNull(); // <4>
assertThat(rundown.stepReports).hasSize(3); // <5>
}
// end::revoman-simple-demo[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class RestfulAPIDevKtTest {
.nodeModulesRelativePath("js")
.off()
)
assertThat(rundown.stepReports).hasSize(3)
assertThat(rundown.firstUnsuccessfulStepReport).isNull()
assertThat(rundown.stepReports).hasSize(3)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ internal interface KickDef {
fun requestConfig(): Set<RequestConfig>

@Value.Derived
fun customTypeAdaptersFromRequestConfig(): Map<Type, List<Either<JsonAdapter<Any>, Factory>>> =
fun customTypeAdaptersFromRequestConfig():
Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> =
requestConfig()
.filter { it.customTypeAdapter != null }
.groupBy({ it.objType }, { it.customTypeAdapter!! })
Expand All @@ -79,7 +80,8 @@ internal interface KickDef {
responseConfig().groupBy { it.ifSuccess }

@Value.Derived
fun customTypeAdaptersFromResponseConfig(): Map<Type, List<Either<JsonAdapter<Any>, Factory>>> =
fun customTypeAdaptersFromResponseConfig():
Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> =
responseConfig()
.filter { it.customTypeAdapter != null }
.groupBy({ it.objType }, { it.customTypeAdapter!! })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ data class RequestConfig
internal constructor(
val preTxnStepPick: PreTxnStepPick,
val objType: Type,
val customTypeAdapter: Either<JsonAdapter<Any>, JsonAdapter.Factory>? = null,
val customTypeAdapter: Either<JsonAdapter<out Any>, JsonAdapter.Factory>? = null,
) {
companion object {
@JvmStatic
Expand All @@ -29,7 +29,7 @@ internal constructor(
fun unmarshallRequest(
preTxnStepPick: PreTxnStepPick,
requestType: Type,
customTypeAdapter: JsonAdapter<Any>,
customTypeAdapter: JsonAdapter<out Any>,
): RequestConfig = RequestConfig(preTxnStepPick, requestType, left(customTypeAdapter))

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal constructor(
val postTxnStepPick: PostTxnStepPick,
val ifSuccess: Boolean?,
val objType: Type,
val customTypeAdapter: Either<JsonAdapter<Any>, JsonAdapter.Factory>? = null,
val customTypeAdapter: Either<JsonAdapter<out Any>, JsonAdapter.Factory>? = null,
) {
companion object {
@JvmStatic
Expand All @@ -30,7 +30,7 @@ internal constructor(
fun unmarshallResponse(
postTxnStepPick: PostTxnStepPick,
successType: Type,
customTypeAdapter: JsonAdapter<Any>,
customTypeAdapter: JsonAdapter<out Any>,
): ResponseConfig = ResponseConfig(postTxnStepPick, null, successType, left(customTypeAdapter))

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fun <PojoT : Any> jsonFileToPojo(
pojoType: Type,
jsonFilePath: String,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
): PojoT? {
val jsonAdapter = initMoshi<PojoT>(customAdapters, customAdaptersWithType, skipTypes, pojoType)
Expand All @@ -52,7 +52,7 @@ fun <PojoT : Any> jsonFileToPojo(jsonFile: JsonFile<PojoT>): PojoT? =
inline fun <reified PojoT : Any> jsonFileToPojo(
jsonFilePath: String,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
): PojoT? =
jsonFileToPojo(PojoT::class.java, jsonFilePath, customAdapters, customAdaptersWithType, skipTypes)
Expand All @@ -73,7 +73,7 @@ fun <PojoT : Any> jsonToPojo(
pojoType: Type,
jsonStr: String,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
): PojoT? {
val jsonAdapter = initMoshi<PojoT>(customAdapters, customAdaptersWithType, skipTypes, pojoType)
Expand All @@ -92,7 +92,7 @@ fun <PojoT : Any> jsonToPojo(jsonString: JsonString<PojoT>): PojoT? =
inline fun <reified PojoT : Any> jsonToPojo(
jsonStr: String,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
): PojoT? =
jsonToPojo(PojoT::class.java, jsonStr, customAdapters, customAdaptersWithType, skipTypes)
Expand All @@ -114,7 +114,7 @@ fun <PojoT : Any> pojoToJson(
pojoType: Type,
pojo: PojoT,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
indent: String? = " ",
): String? {
Expand All @@ -135,7 +135,7 @@ fun <PojoT : Any> pojoToJson(config: Pojo<PojoT>): String? =
inline fun <reified PojoT : Any> pojoToJson(
pojo: PojoT,
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
indent: String? = " ",
): String? =
Expand All @@ -144,7 +144,7 @@ inline fun <reified PojoT : Any> pojoToJson(
@SuppressWarnings("kotlin:S3923")
private fun <PojoT : Any> initMoshi(
customAdapters: List<Any> = emptyList(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<Any>, Factory>>> = emptyMap(),
customAdaptersWithType: Map<Type, List<Either<JsonAdapter<out Any>, Factory>>> = emptyMap(),
skipTypes: Set<Class<out Any>> = emptySet(),
pojoType: Type,
): JsonAdapter<PojoT> =
Expand All @@ -159,7 +159,7 @@ internal interface PojoDef<PojoT> {

fun customAdapters(): List<Any>

fun customAdaptersWithType(): Map<Type, List<Either<JsonAdapter<Any>, Factory>>>
fun customAdaptersWithType(): Map<Type, List<Either<JsonAdapter<out Any>, Factory>>>

fun skipTypes(): Set<Class<out Any>>

Expand All @@ -186,7 +186,7 @@ internal interface JsonConfig<PojoT> {

fun customAdapters(): List<Any>

fun customAdaptersWithType(): Map<Type, List<Either<JsonAdapter<Any>, Factory>>>
fun customAdaptersWithType(): Map<Type, List<Either<JsonAdapter<out Any>, Factory>>>

fun skipTypes(): Set<Class<out Any>>
}
Expand Down
Loading

0 comments on commit b095a91

Please sign in to comment.