From 41caef6c73f2c1067220c5ba83ebeb73efb2157d Mon Sep 17 00:00:00 2001 From: Matt Fellows <53900+mefellows@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:30:02 +1100 Subject: [PATCH] feat: add pactffi_given_with_params for params Will eventually replace pactffi_given_with_param with pactffi_given_with_params. See https://github.com/pact-foundation/pact-js/issues/848 for background --- native/addon.cc | 3 ++- native/consumer.cc | 58 +++++++++++++++++++++++++++++++++++++++++++ native/consumer.h | 1 + src/consumer/index.ts | 8 ++++++ src/consumer/types.ts | 3 +++ src/ffi/types.ts | 10 ++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/native/addon.cc b/native/addon.cc index 8701cba0..5b6c5bd2 100644 --- a/native/addon.cc +++ b/native/addon.cc @@ -21,7 +21,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "pactffiNewInteraction"), Napi::Function::New(env, PactffiNewInteraction)); exports.Set(Napi::String::New(env, "pactffiUponReceiving"), Napi::Function::New(env, PactffiUponReceiving)); exports.Set(Napi::String::New(env, "pactffiGiven"), Napi::Function::New(env, PactffiGiven)); - exports.Set(Napi::String::New(env, "pactffiGivenWithParam"), Napi::Function::New(env, PactffiGivenWithParam)); + exports.Set(Napi::String::New(env, "PactffiGivenWithParam"), Napi::Function::New(env, PactffiGivenWithParam)); + exports.Set(Napi::String::New(env, "PactffiGivenWithParams"), Napi::Function::New(env, PactffiGivenWithParams)); exports.Set(Napi::String::New(env, "pactffiWithRequest"), Napi::Function::New(env, PactffiWithRequest)); exports.Set(Napi::String::New(env, "pactffiWithQueryParameter"), Napi::Function::New(env, PactffiWithQueryParameter)); exports.Set(Napi::String::New(env, "pactffiWithSpecification"), Napi::Function::New(env, PactffiWithSpecification)); diff --git a/native/consumer.cc b/native/consumer.cc index 9548c101..eaf3a5d9 100644 --- a/native/consumer.cc +++ b/native/consumer.cc @@ -678,6 +678,64 @@ Napi::Value PactffiGivenWithParam(const Napi::CallbackInfo& info) { return Napi::Boolean::New(env, res); } +/** + * Adds a provider state to the Interaction with a set of parameter key and value pairs in JSON + * form. If the params is not an JSON object, it will add it as a single parameter with a `value` + * key. + * + * # Parameters + * * `description` - The provider state description. + * * `params` - Parameter values as a JSON fragment. + * + * # Errors + * Returns EXIT_FAILURE (1) if the interaction or Pact can't be modified (i.e. the mock server + * for it has already started). + * Returns 2 and sets the error message (which can be retrieved with `pactffi_get_error_message`) + * if the parameter values con't be parsed as JSON. + * Returns 3 if any of the C strings are not valid. + * + * + * C interface: + * + * int pactffi_given_with_params(InteractionHandle interaction, + * const char *description, + * const char *params); + */ +Napi::Value PactffiGivenWithParams(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() < 4) { + throw Napi::Error::New(env, "PactffiGivenWithParams received < 4 arguments"); + } + + if (!info[0].IsNumber()) { + throw Napi::Error::New(env, "PactffiGivenWithParams(arg 0) expected a InteractionHandle (uint32_t)"); + } + + if (!info[1].IsString()) { + throw Napi::Error::New(env, "PactffiGivenWithParams(arg 1) expected a string"); + } + + if (!info[2].IsString()) { + throw Napi::Error::New(env, "PactffiGivenWithParams(arg 2) expected a string"); + } + + if (!info[3].IsString()) { + throw Napi::Error::New(env, "PactffiGivenWithParams(arg 3) expected a string"); + } + + InteractionHandle interaction = info[0].As().Uint32Value(); + std::string description = info[1].As().Utf8Value(); + std::string params = info[2].As().Utf8Value(); + + int res = pactffi_given_with_params(interaction, description.c_str(), params.c_str()); + + if (res > 0) { + return Napi::Boolean::New(env, false); + } + return Napi::Boolean::New(env, true); +} + /** * Configures the request for the Interaction. Returns false if the interaction or Pact can't be * modified (i.e. the mock server for it has already started) diff --git a/native/consumer.h b/native/consumer.h index bd5d7c93..1f7f2441 100644 --- a/native/consumer.h +++ b/native/consumer.h @@ -17,6 +17,7 @@ Napi::Value PactffiCreateMockServerForPact(const Napi::CallbackInfo& info); Napi::Value PactffiCreateMockServer(const Napi::CallbackInfo& info); Napi::Value PactffiGiven(const Napi::CallbackInfo& info); Napi::Value PactffiGivenWithParam(const Napi::CallbackInfo& info); +Napi::Value PactffiGivenWithParams(const Napi::CallbackInfo& info); Napi::Value PactffiMockServerLogs(const Napi::CallbackInfo& info); Napi::Value PactffiMockServerMatched(const Napi::CallbackInfo& info); Napi::Value PactffiMockServerMismatches(const Napi::CallbackInfo& info); diff --git a/src/consumer/index.ts b/src/consumer/index.ts index 9011e281..57ff5517 100644 --- a/src/consumer/index.ts +++ b/src/consumer/index.ts @@ -42,6 +42,8 @@ const asyncMessage = (ffi: Ffi, interactionPtr: number) => ({ given: (state: string) => ffi.pactffiMessageGiven(interactionPtr, state), givenWithParam: (state: string, name: string, value: string) => ffi.pactffiMessageGivenWithParam(interactionPtr, state, name, value), + givenWithParams: (state: string, params: string) => + ffi.pactffiMessageGivenWithParams(interactionPtr, state, params), withContents: (body: string, contentType: string) => ffi.pactffiMessageWithContents(interactionPtr, contentType, body), withBinaryContents: (body: Buffer, contentType: string) => @@ -181,6 +183,8 @@ export const makeConsumerPact = ( given: (state: string) => ffi.pactffiGiven(interactionPtr, state), givenWithParam: (state: string, name: string, value: string) => ffi.pactffiGivenWithParam(interactionPtr, state, name, value), + givenWithParams: (state: string, params: string) => + ffi.pactffiGivenWithParams(interactionPtr, state, params), withRequestContents: (body: string, contentType: string) => ffi.pactffiWithBody( interactionPtr, @@ -241,6 +245,8 @@ export const makeConsumerPact = ( given: (state: string) => ffi.pactffiGiven(interactionPtr, state), givenWithParam: (state: string, name: string, value: string) => ffi.pactffiGivenWithParam(interactionPtr, state, name, value), + givenWithParams: (state: string, params: string) => + ffi.pactffiGivenWithParams(interactionPtr, state, params), withRequest: (method: string, path: string) => ffi.pactffiWithRequest(interactionPtr, method, path), withQuery: (name: string, index: number, value: string) => @@ -451,6 +457,8 @@ export const makeConsumerMessagePact = ( given: (state: string) => ffi.pactffiGiven(interactionPtr, state), givenWithParam: (state: string, name: string, value: string) => ffi.pactffiGivenWithParam(interactionPtr, state, name, value), + givenWithParams: (state: string, params: string) => + ffi.pactffiGivenWithParams(interactionPtr, state, params), withRequestContents: (body: string, contentType: string) => ffi.pactffiWithBody( interactionPtr, diff --git a/src/consumer/types.ts b/src/consumer/types.ts index acc66e8c..bb8c4f9a 100644 --- a/src/consumer/types.ts +++ b/src/consumer/types.ts @@ -161,6 +161,7 @@ export type ConsumerInteraction = PluginInteraction & { uponReceiving: (description: string) => boolean; given: (state: string) => boolean; givenWithParam: (state: string, name: string, value: string) => boolean; + givenWithParams: (state: string, params: string) => boolean; withRequest: (method: string, path: string) => boolean; withQuery: (name: string, index: number, value: string) => boolean; withStatus: (status: number) => boolean; @@ -233,6 +234,7 @@ export type ConsumerPact = PluginPact & { export type AsynchronousMessage = RequestPluginInteraction & { given: (state: string) => void; givenWithParam: (state: string, name: string, value: string) => void; + givenWithParams: (state: string, params: string) => void; expectsToReceive: (description: string) => void; withMetadata: (name: string, value: string) => void; withContents: (body: string, contentType: string) => void; @@ -245,6 +247,7 @@ export type ConsumerMessage = AsynchronousMessage; export type SynchronousMessage = PluginInteraction & { given: (state: string) => void; givenWithParam: (state: string, name: string, value: string) => void; + givenWithParams: (state: string, params: string) => void; withMetadata: (name: string, value: string) => void; withRequestContents: (body: string, contentType: string) => void; withResponseContents: (body: string, contentType: string) => void; diff --git a/src/ffi/types.ts b/src/ffi/types.ts index 85a94451..9adcadab 100644 --- a/src/ffi/types.ts +++ b/src/ffi/types.ts @@ -163,6 +163,11 @@ export type FfiConsumerFunctions = { name: string, value: string ): boolean; + pactffiGivenWithParams( + handle: FfiInteractionHandle, + description: string, + params: string + ): boolean; pactffiWithRequest( handle: FfiInteractionHandle, method: string, @@ -278,6 +283,11 @@ export type FfiConsumerFunctions = { key: string, value: string ): void; + pactffiMessageGivenWithParams( + handle: FfiMessageHandle, + description: string, + params: string + ): void; pactffiMessageWithContents( handle: FfiMessageHandle, contentType: string,