ci(runner): rewrite collection_runner.sh in rust (#1604)

This commit is contained in:
Pa1NarK
2023-07-10 15:02:44 +05:30
committed by GitHub
parent 69454ec55c
commit e09077a75f
17 changed files with 496 additions and 240 deletions

Binary file not shown.

View File

@ -1,9 +1,9 @@
name: Postman Collection API test
name: Postman Collection Runner
on:
workflow_dispatch:
schedule:
- cron: '30 0,12 * * *' # Run workflow at 6 AM and 6 PM IST
- cron: "30 0,12 * * *" # Run workflow at 6 AM and 6 PM IST
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -33,7 +33,14 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2.4.0
- name: Decrypt connector auth file
env:
CONNECTOR_AUTH_PASSPHRASE: ${{ secrets.CONNECTOR_AUTH_PASSPHRASE }}
@ -48,13 +55,14 @@ jobs:
env:
ADMIN_API_KEY: ${{ secrets.INTEG_ADMIN_API_KEY }}
BASE_URL: ${{ secrets.INTEG_BASE_URL }}
CONNECTOR_CONFIG_PATH: ${{ env.CONNECTOR_CONFIG_PATH }}
CONNECTOR_AUTH_FILE_PATH: ${{ env.CONNECTOR_CONFIG_PATH }}
GATEWAY_MERCHANT_ID: ${{ secrets.STRIPE_GATEWAY_MERCHANT_ID }}
GPAY_CERTIFICATE: ${{ secrets.STRIPE_GPAY_CERTIFICATE }}
GPAY_CERTIFICATE_KEYS: ${{ secrets.STRIPE_GPAY_CERTIFICATE_KEYS }}
uses: nick-fields/retry@v2
with:
timeout_minutes: 6
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: ./scripts/postman_test_automation.sh ${{ matrix.connector }}
command: |
cargo run --package test_utils --bin test_utils -- --connector_name=${{ matrix.connector }} --base_url=$BASE_URL --admin_api_key=$ADMIN_API_KEY

16
Cargo.lock generated
View File

@ -1312,6 +1312,7 @@ dependencies = [
"anstyle",
"bitflags 1.3.2",
"clap_lex",
"once_cell",
]
[[package]]
@ -3819,6 +3820,7 @@ dependencies = [
"signal-hook-tokio",
"storage_models",
"strum",
"test_utils",
"thirtyfour",
"thiserror",
"time 0.3.22",
@ -4531,6 +4533,20 @@ dependencies = [
"test-case-core",
]
[[package]]
name = "test_utils"
version = "0.1.0"
dependencies = [
"api_models",
"clap",
"masking",
"router",
"serde",
"serde_json",
"serde_path_to_error",
"toml 0.7.4",
]
[[package]]
name = "thirtyfour"
version = "0.31.0"

View File

@ -2,10 +2,8 @@ use std::str::FromStr;
use masking::Secret;
use router::types::{self, api, storage::enums};
use crate::{
connector_auth,
utils::{self, ConnectorActions},
};
use crate::utils::{self, ConnectorActions};
use test_utils::connector_auth;
#[derive(Clone, Copy)]
struct {{project-name | downcase | pascal_case}}Test;

Binary file not shown.

View File

@ -33,7 +33,7 @@ signal-hook = { version = "0.3.15", optional = true }
thiserror = "1.0.40"
time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] }
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"], optional = true }
phonenumber = "0.3.2+8.13.9"
phonenumber = "0.3.2"
# First party crates
masking = { version = "0.1.0", path = "../masking" }

View File

@ -114,6 +114,9 @@ tokio = "1.28.2"
toml = "0.7.4"
wiremock = "0.5"
# First party dev-dependencies
test_utils = { version = "0.1.0", path = "../test_utils" }
[[bin]]
name = "router"
path = "src/bin/router.rs"

View File

@ -1,149 +0,0 @@
use std::env;
use router::types::ConnectorAuthType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ConnectorAuthentication {
pub aci: Option<BodyKey>,
pub adyen: Option<BodyKey>,
pub adyen_uk: Option<BodyKey>,
pub airwallex: Option<BodyKey>,
pub authorizedotnet: Option<BodyKey>,
pub bambora: Option<BodyKey>,
pub bitpay: Option<HeaderKey>,
pub bluesnap: Option<BodyKey>,
pub cashtocode: Option<BodyKey>,
pub checkout: Option<SignatureKey>,
pub coinbase: Option<HeaderKey>,
pub cryptopay: Option<BodyKey>,
pub cybersource: Option<SignatureKey>,
pub dlocal: Option<SignatureKey>,
#[cfg(feature = "dummy_connector")]
pub dummyconnector: Option<HeaderKey>,
pub fiserv: Option<SignatureKey>,
pub forte: Option<MultiAuthKey>,
pub globalpay: Option<BodyKey>,
pub globepay: Option<HeaderKey>,
pub iatapay: Option<SignatureKey>,
pub mollie: Option<HeaderKey>,
pub multisafepay: Option<HeaderKey>,
pub nexinets: Option<BodyKey>,
pub noon: Option<SignatureKey>,
pub nmi: Option<HeaderKey>,
pub nuvei: Option<SignatureKey>,
pub opayo: Option<HeaderKey>,
pub opennode: Option<HeaderKey>,
pub payeezy: Option<SignatureKey>,
pub payme: Option<BodyKey>,
pub paypal: Option<BodyKey>,
pub payu: Option<BodyKey>,
pub powertranz: Option<HeaderKey>,
pub rapyd: Option<BodyKey>,
pub shift4: Option<HeaderKey>,
pub stripe: Option<HeaderKey>,
pub stripe_au: Option<HeaderKey>,
pub stripe_uk: Option<HeaderKey>,
pub trustpay: Option<SignatureKey>,
pub worldpay: Option<BodyKey>,
pub worldline: Option<SignatureKey>,
pub zen: Option<HeaderKey>,
pub automation_configs: Option<AutomationConfigs>,
}
impl ConnectorAuthentication {
#[allow(clippy::expect_used)]
pub(crate) fn new() -> Self {
// before running tests
let path = env::var("CONNECTOR_AUTH_FILE_PATH")
.expect("connector authentication file path not set");
toml::from_str(
&std::fs::read_to_string(path).expect("connector authentication config file not found"),
)
.expect("Failed to read connector authentication config file")
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HeaderKey {
pub api_key: String,
}
impl From<HeaderKey> for ConnectorAuthType {
fn from(key: HeaderKey) -> Self {
Self::HeaderKey {
api_key: key.api_key,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BodyKey {
pub api_key: String,
pub key1: String,
}
impl From<BodyKey> for ConnectorAuthType {
fn from(key: BodyKey) -> Self {
Self::BodyKey {
api_key: key.api_key,
key1: key.key1,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SignatureKey {
pub api_key: String,
pub key1: String,
pub api_secret: String,
}
impl From<SignatureKey> for ConnectorAuthType {
fn from(key: SignatureKey) -> Self {
Self::SignatureKey {
api_key: key.api_key,
key1: key.key1,
api_secret: key.api_secret,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MultiAuthKey {
pub api_key: String,
pub key1: String,
pub api_secret: String,
pub key2: String,
}
impl From<MultiAuthKey> for ConnectorAuthType {
fn from(key: MultiAuthKey) -> Self {
Self::MultiAuthKey {
api_key: key.api_key,
key1: key.key1,
api_secret: key.api_secret,
key2: key.key2,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AutomationConfigs {
pub hs_base_url: Option<String>,
pub hs_api_key: Option<String>,
pub hs_test_browser: Option<String>,
pub chrome_profile_path: Option<String>,
pub firefox_profile_path: Option<String>,
pub pypl_email: Option<String>,
pub pypl_pass: Option<String>,
pub gmail_email: Option<String>,
pub gmail_pass: Option<String>,
pub configs_url: Option<String>,
pub stripe_pub_key: Option<String>,
pub testcases_path: Option<String>,
pub bluesnap_gateway_merchant_id: Option<String>,
pub globalpay_gateway_merchant_id: Option<String>,
pub run_minimum_steps: Option<bool>,
pub airwallex_merchant_name: Option<String>,
}

View File

@ -3,11 +3,9 @@ use std::str::FromStr;
use cards::CardNumber;
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;
use crate::{
connector_auth,
utils::{self, ConnectorActions},
};
use crate::utils::{self, ConnectorActions};
#[derive(Clone, Copy)]
struct DummyConnectorTest;

View File

@ -4,6 +4,7 @@
clippy::unwrap_in_result,
clippy::unwrap_used
)]
use test_utils::connector_auth;
mod aci;
mod adyen;
@ -20,7 +21,6 @@ mod cashtocode;
mod checkout;
mod checkout_ui;
mod coinbase;
mod connector_auth;
mod cryptopay;
mod cybersource;
mod dlocal;

View File

@ -1,13 +1,8 @@
#![allow(clippy::unwrap_used)]
mod utils;
#[allow(dead_code)]
mod auth {
include!("connectors/connector_auth.rs");
}
use auth::ConnectorAuthentication;
use masking::PeekInterface;
use test_utils::connector_auth::ConnectorAuthentication;
use utils::{mk_service, ApiKey, AppClient, MerchantId, PaymentId, Status};
/// Example of unit test
@ -77,7 +72,7 @@ async fn partial_refund() {
&server,
&merchant_id,
"stripe",
&authentication.checkout.unwrap().api_key,
authentication.checkout.unwrap().api_key.peek(),
)
.await;
@ -143,7 +138,7 @@ async fn exceed_refund() {
&server,
&merchant_id,
"stripe",
&authentication.checkout.unwrap().api_key,
authentication.checkout.unwrap().api_key.peek(),
)
.await;

View File

@ -0,0 +1,25 @@
[package]
name = "test_utils"
description = "Postman collection runner and utility"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
readme = "README.md"
license.workspace = true
[features]
default = ["dummy_connector"]
dummy_connector = ["api_models/dummy_connector"]
[dependencies]
clap = { version = "4.3.2", default-features = false, features = ["std", "derive", "help", "usage", "cargo"] }
serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96"
serde_path_to_error = "0.1.11"
toml = "0.7.4"
# First party crates
router = { version = "0.2.0", path = "../router" }
api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] }
masking = { version = "0.1.0", path = "../masking" }

View File

@ -0,0 +1,13 @@
# Test Runner
The main part of running tests through `newman`.
# Usage
- Make sure you that you've the postman collection for the connector available in the `postman` dir with the name `<connector_name>.postman_collection.json`
- Add the connector credentials to the `connector_auth.toml` / `auth.toml`
- In terminal, execute:
```zsh
export CONNECTOR_AUTH_FILE_PATH=/path/to/auth.toml
cargo run --package test_utils --bin test_utils -- --connector_name=<connector_name> --base_url=<base_url> --admin_api_key=<admin_api_key>
```

View File

@ -0,0 +1,258 @@
use std::{collections::HashMap, env};
use masking::{PeekInterface, Secret};
use router::types::ConnectorAuthType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ConnectorAuthentication {
pub aci: Option<BodyKey>,
pub adyen: Option<BodyKey>,
pub adyen_uk: Option<BodyKey>,
pub airwallex: Option<BodyKey>,
pub authorizedotnet: Option<BodyKey>,
pub bambora: Option<BodyKey>,
pub bitpay: Option<HeaderKey>,
pub bluesnap: Option<BodyKey>,
pub cashtocode: Option<BodyKey>,
pub checkout: Option<SignatureKey>,
pub coinbase: Option<HeaderKey>,
pub cryptopay: Option<BodyKey>,
pub cybersource: Option<SignatureKey>,
pub dlocal: Option<SignatureKey>,
#[cfg(feature = "dummy_connector")]
pub dummyconnector: Option<HeaderKey>,
pub fiserv: Option<SignatureKey>,
pub forte: Option<MultiAuthKey>,
pub globalpay: Option<BodyKey>,
pub globepay: Option<HeaderKey>,
pub iatapay: Option<SignatureKey>,
pub mollie: Option<HeaderKey>,
pub multisafepay: Option<HeaderKey>,
pub nexinets: Option<BodyKey>,
pub noon: Option<SignatureKey>,
pub nmi: Option<HeaderKey>,
pub nuvei: Option<SignatureKey>,
pub opayo: Option<HeaderKey>,
pub opennode: Option<HeaderKey>,
pub payeezy: Option<SignatureKey>,
pub payme: Option<BodyKey>,
pub paypal: Option<BodyKey>,
pub payu: Option<BodyKey>,
pub powertranz: Option<HeaderKey>,
pub rapyd: Option<BodyKey>,
pub shift4: Option<HeaderKey>,
pub stripe: Option<HeaderKey>,
pub stripe_au: Option<HeaderKey>,
pub stripe_uk: Option<HeaderKey>,
pub trustpay: Option<SignatureKey>,
pub worldpay: Option<BodyKey>,
pub worldline: Option<SignatureKey>,
pub zen: Option<HeaderKey>,
pub automation_configs: Option<AutomationConfigs>,
}
impl Default for ConnectorAuthentication {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
impl ConnectorAuthentication {
#[allow(clippy::expect_used)]
pub fn new() -> Self {
// Do `export CONNECTOR_AUTH_FILE_PATH="/hyperswitch/crates/router/tests/connectors/sample_auth.toml"`
// before running tests in shell
let path = env::var("CONNECTOR_AUTH_FILE_PATH")
.expect("Connector authentication file path not set");
toml::from_str(
&std::fs::read_to_string(path).expect("connector authentication config file not found"),
)
.expect("Failed to read connector authentication config file")
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct ConnectorAuthenticationMap(HashMap<String, ConnectorAuthType>);
impl Default for ConnectorAuthenticationMap {
fn default() -> Self {
Self::new()
}
}
// This is a temporary solution to avoid rust compiler from complaining about unused function
#[allow(dead_code)]
impl ConnectorAuthenticationMap {
pub fn inner(&self) -> &HashMap<String, ConnectorAuthType> {
&self.0
}
#[allow(clippy::expect_used)]
pub fn new() -> Self {
// Do `export CONNECTOR_AUTH_FILE_PATH="/hyperswitch/crates/router/tests/connectors/sample_auth.toml"`
// before running tests in shell
let path = env::var("CONNECTOR_AUTH_FILE_PATH")
.expect("connector authentication file path not set");
// Read the file contents to a JsonString
let contents =
&std::fs::read_to_string(path).expect("Failed to read connector authentication file");
// Deserialize the JsonString to a HashMap
let auth_config: HashMap<String, toml::Value> =
toml::from_str(contents).expect("Failed to deserialize TOML file");
// auth_config contains the data in below given format:
// {
// "connector_name": Table(
// {
// "api_key": String(
// "API_Key",
// ),
// "api_secret": String(
// "Secret key",
// ),
// "key1": String(
// "key1",
// ),
// "key2": String(
// "key2",
// ),
// },
// ),
// "connector_name": Table(
// ...
// }
// auth_map refines and extracts required information
let auth_map = auth_config
.into_iter()
.map(|(connector_name, config)| {
let auth_type = match config {
toml::Value::Table(table) => {
match (
table.get("api_key"),
table.get("key1"),
table.get("api_secret"),
table.get("key2"),
) {
(Some(api_key), None, None, None) => ConnectorAuthType::HeaderKey {
api_key: api_key.as_str().unwrap_or_default().to_string(),
},
(Some(api_key), Some(key1), None, None) => ConnectorAuthType::BodyKey {
api_key: api_key.as_str().unwrap_or_default().to_string(),
key1: key1.as_str().unwrap_or_default().to_string(),
},
(Some(api_key), Some(key1), Some(api_secret), None) => {
ConnectorAuthType::SignatureKey {
api_key: api_key.as_str().unwrap_or_default().to_string(),
key1: key1.as_str().unwrap_or_default().to_string(),
api_secret: api_secret.as_str().unwrap_or_default().to_string(),
}
}
(Some(api_key), Some(key1), Some(api_secret), Some(key2)) => {
ConnectorAuthType::MultiAuthKey {
api_key: api_key.as_str().unwrap_or_default().to_string(),
key1: key1.as_str().unwrap_or_default().to_string(),
api_secret: api_secret.as_str().unwrap_or_default().to_string(),
key2: key2.as_str().unwrap_or_default().to_string(),
}
}
_ => ConnectorAuthType::NoKey,
}
}
_ => ConnectorAuthType::NoKey,
};
(connector_name, auth_type)
})
.collect();
Self(auth_map)
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HeaderKey {
pub api_key: Secret<String>,
}
impl From<HeaderKey> for ConnectorAuthType {
fn from(key: HeaderKey) -> Self {
Self::HeaderKey {
api_key: key.api_key.peek().to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BodyKey {
pub api_key: Secret<String>,
pub key1: Secret<String>,
}
impl From<BodyKey> for ConnectorAuthType {
fn from(key: BodyKey) -> Self {
Self::BodyKey {
api_key: key.api_key.peek().to_string(),
key1: key.key1.peek().to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SignatureKey {
pub api_key: Secret<String>,
pub key1: Secret<String>,
pub api_secret: Secret<String>,
}
impl From<SignatureKey> for ConnectorAuthType {
fn from(key: SignatureKey) -> Self {
Self::SignatureKey {
api_key: key.api_key.peek().to_string(),
key1: key.key1.peek().to_string(),
api_secret: key.api_secret.peek().to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MultiAuthKey {
pub api_key: Secret<String>,
pub key1: Secret<String>,
pub api_secret: Secret<String>,
pub key2: Secret<String>,
}
impl From<MultiAuthKey> for ConnectorAuthType {
fn from(key: MultiAuthKey) -> Self {
Self::MultiAuthKey {
api_key: key.api_key.peek().to_string(),
key1: key.key1.peek().to_string(),
api_secret: key.api_secret.peek().to_string(),
key2: key.key2.peek().to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AutomationConfigs {
pub hs_base_url: Option<String>,
pub hs_api_key: Option<String>,
pub hs_test_browser: Option<String>,
pub chrome_profile_path: Option<String>,
pub firefox_profile_path: Option<String>,
pub pypl_email: Option<String>,
pub pypl_pass: Option<String>,
pub gmail_email: Option<String>,
pub gmail_pass: Option<String>,
pub configs_url: Option<String>,
pub stripe_pub_key: Option<String>,
pub testcases_path: Option<String>,
pub bluesnap_gateway_merchant_id: Option<String>,
pub globalpay_gateway_merchant_id: Option<String>,
pub run_minimum_steps: Option<bool>,
pub airwallex_merchant_name: Option<String>,
}

View File

@ -0,0 +1 @@
pub mod connector_auth;

View File

@ -0,0 +1,156 @@
use std::{
env,
process::{exit, Command as cmd},
};
use clap::{arg, command, Parser};
use router::types::ConnectorAuthType;
use test_utils::connector_auth::ConnectorAuthenticationMap;
// Just by the name of the connector, this function generates the name of the collection
// Example: CONNECTOR_NAME="stripe" -> OUTPUT: postman/stripe.postman_collection.json
#[inline]
fn path_generation(name: impl AsRef<str>) -> String {
format!("postman/{}.postman_collection.json", name.as_ref())
}
#[derive(Parser)]
#[command(version, about = "Postman collection runner using newman!", long_about = None)]
struct Args {
/// Name of the connector
#[arg(short, long = "connector_name")]
connector_name: String,
/// Base URL of the Hyperswitch environment
#[arg(short, long = "base_url")]
base_url: String,
/// Admin API Key of the environment
#[arg(short, long = "admin_api_key")]
admin_api_key: String,
}
fn main() {
let args = Args::parse();
let connector_name = args.connector_name;
let base_url = args.base_url;
let admin_api_key = args.admin_api_key;
let collection_path = path_generation(&connector_name);
let auth_map = ConnectorAuthenticationMap::new();
let inner_map = auth_map.inner();
// Newman runner
// Depending on the conditions satisfied, variables are added. Since certificates of stripe have already
// been added to the postman collection, those conditions are set to true and collections that have
// variables set up for certificate, will consider those variables and will fail.
let mut newman_command = cmd::new("newman");
newman_command.args(["run", &collection_path]);
newman_command.args(["--env-var", &format!("admin_api_key={admin_api_key}")]);
newman_command.args(["--env-var", &format!("baseUrl={base_url}")]);
if let Some(auth_type) = inner_map.get(&connector_name) {
match auth_type {
ConnectorAuthType::HeaderKey { api_key } => {
newman_command.args(["--env-var", &format!("connector_api_key={api_key}")]);
}
ConnectorAuthType::BodyKey { api_key, key1 } => {
newman_command.args([
"--env-var",
&format!("connector_api_key={api_key}"),
"--env-var",
&format!("connector_key1={key1}"),
]);
}
ConnectorAuthType::SignatureKey {
api_key,
key1,
api_secret,
} => {
newman_command.args([
"--env-var",
&format!("connector_api_key={api_key}"),
"--env-var",
&format!("connector_key1={key1}"),
"--env-var",
&format!("connector_api_secret={api_secret}"),
]);
}
ConnectorAuthType::MultiAuthKey {
api_key,
key1,
key2,
api_secret,
} => {
newman_command.args([
"--env-var",
&format!("connector_api_key={api_key}"),
"--env-var",
&format!("connector_key1={key1}"),
"--env-var",
&format!("connector_key1={key2}"),
"--env-var",
&format!("connector_api_secret={api_secret}"),
]);
}
// Handle other ConnectorAuthType variants
_ => {
eprintln!("Invalid authentication type.");
}
}
} else {
eprintln!("Connector not found.");
}
// Add additional environment variables if present
if let Ok(gateway_merchant_id) = env::var("GATEWAY_MERCHANT_ID") {
newman_command.args([
"--env-var",
&format!("gateway_merchant_id={gateway_merchant_id}"),
]);
}
if let Ok(gpay_certificate) = env::var("GPAY_CERTIFICATE") {
newman_command.args(["--env-var", &format!("certificate={gpay_certificate}")]);
}
if let Ok(gpay_certificate_keys) = env::var("GPAY_CERTIFICATE_KEYS") {
newman_command.args([
"--env-var",
&format!("certificate_keys={gpay_certificate_keys}"),
]);
}
newman_command.arg("--delay-request").arg("5");
// Execute the newman command
let output = newman_command.spawn();
let mut child = match output {
Ok(child) => child,
Err(err) => {
eprintln!("Failed to execute command: {err}");
exit(1);
}
};
let status = child.wait();
let exit_code = match status {
Ok(exit_status) => {
if exit_status.success() {
println!("Command executed successfully!");
exit_status.code().unwrap_or(0)
} else {
eprintln!("Command failed with exit code: {:?}", exit_status.code());
exit_status.code().unwrap_or(1)
}
}
Err(err) => {
eprintln!("Failed to wait for command execution: {}", err);
exit(1);
}
};
exit(exit_code);
}

View File

@ -1,66 +0,0 @@
#! /usr/bin/env bash
set -euo pipefail
# Just by the name of the connector, this function generates the name of the collection
# Example: CONNECTOR_NAME="stripe" -> OUTPUT: postman/stripe.postman_collection.json
path_generation() {
local name="${1}"
local collection_name="postman/${name}.postman_collection.json"
echo "${collection_name}"
}
# This function gets the api keys from the connector_auth.toml file
# Also determines the type of key (HeaderKey, BodyKey, SignatureKey) for the connector
get_api_keys() {
local input="${1}"
# We get $CONNECTOR_CONFIG_PATH from the GITHUB_ENV
result=$(awk -v name="${input}" -F ' // ' 'BEGIN{ flag=0 } /^\[.*\]/{ if ($1 == "["name"]") { flag=1 } else { flag=0 } } flag==1 && /^[^#]/ { print $0 }' "${CONNECTOR_CONFIG_PATH}")
# OUTPUT of result for `<connector_name>` that has `HeaderKey`:
# [<connector_name>]
# api_key = "HeadKey of <connector_name>"
# Keys are set as variables since some API Keys for connectors such as ACI
# are Base64 encoded and require "Bearer" to be prefixed such as "Bearer Skst45645gey5r#&$==".
# This effectively stops the shell from interpreting the value of the variable as a command.
API_KEY=$(echo "${result}" | awk -F ' = ' '$1 == "api_key" { gsub(/"/, "", $2); print $2 }')
KEY1=$(echo "${result}" | awk -F ' = ' '$1 == "key1" { gsub(/"/, "", $2); print $2 }')
KEY2=$(echo "${result}" | awk -F ' = ' '$1 == "key2" { gsub(/"/, "", $2); print $2 }')
API_SECRET=$(echo "${result}" | awk -F ' = ' '$1 == "api_secret" { gsub(/"/, "", $2); print $2 }')
# Determine the type of key
if [[ -n "${API_KEY}" && -z "${KEY1}" && -z "${API_SECRET}" ]]; then
KEY_TYPE="HeaderKey"
elif [[ -n "${API_KEY}" && -n "${KEY1}" && -z "${API_SECRET}" ]]; then
KEY_TYPE="BodyKey"
elif [[ -n "${API_KEY}" && -n "${KEY1}" && -n "${API_SECRET}" ]]; then
KEY_TYPE="SignatureKey"
elif [[ -n "${API_KEY}" && -n "${KEY1}" && -n "${KEY2}" && -n "${API_SECRET}" ]]; then
KEY_TYPE="MultiAuthKey"
else
KEY_TYPE="Invalid"
fi
}
# [ MAIN ]
CONNECTOR_NAME="${1}"
KEY_TYPE=""
# Function call
COLLECTION_PATH=$(path_generation "${CONNECTOR_NAME}")
get_api_keys "${CONNECTOR_NAME}"
# Newman runner
# Depending on the conditions satisfied, variables are added. Since certificates of stripe have already
# been added to the postman collection, those conditions are set to true and collections that have
# variables set up for certificate, will consider those variables and will fail.
newman run "${COLLECTION_PATH}" \
--env-var "admin_api_key=${ADMIN_API_KEY}" \
--env-var "baseUrl=${BASE_URL}" \
--env-var "connector_api_key=${API_KEY}" \
$(if [[ "${KEY_TYPE}" == BodyKey ]]; then echo --env-var "connector_key1=${KEY1}"; fi) \
$(if [[ "${KEY_TYPE}" == SignatureKey ]]; then echo --env-var "connector_key1=${KEY1}" --env-var "connector_api_secret=${API_SECRET}"; fi) \
$(if [[ "${KEY_TYPE}" == MultiAuthKey ]]; then echo --env-var "connector_key1=${KEY1}" --env-var "connector_key2=${KEY2}" --env-var "connector_api_secret=${API_SECRET}"; fi) \
$(if [[ -n "${GATEWAY_MERCHANT_ID}" ]]; then echo --env-var "gateway_merchant_id=${GATEWAY_MERCHANT_ID}"; fi) \
$(if [[ -n "${GPAY_CERTIFICATE}" ]]; then echo --env-var "certificate=${GPAY_CERTIFICATE}"; fi) \
$(if [[ -n "${GPAY_CERTIFICATE_KEYS}" ]]; then echo --env-var "certificate_keys=${GPAY_CERTIFICATE_KEYS}"; fi) \
--delay-request 5