From 3325d97d00ee60b3c8817c5ed6971cefd2074305 Mon Sep 17 00:00:00 2001 From: Pete Cornish Date: Wed, 16 Oct 2024 10:17:03 +0100 Subject: [PATCH] feat: allows random generator to be used in scripts. --- .../script/ScriptedResponseServiceImpl.kt | 11 +-- .../expression/eval/RandomEvaluator.kt | 31 ++----- .../expression/helper/RandomHelper.kt | 89 +++++++++++++++++++ 3 files changed, 103 insertions(+), 28 deletions(-) create mode 100644 core/expression/src/main/java/io/gatehill/imposter/expression/helper/RandomHelper.kt diff --git a/core/engine/src/main/java/io/gatehill/imposter/service/script/ScriptedResponseServiceImpl.kt b/core/engine/src/main/java/io/gatehill/imposter/service/script/ScriptedResponseServiceImpl.kt index 87338dc4e..238b4fc4b 100644 --- a/core/engine/src/main/java/io/gatehill/imposter/service/script/ScriptedResponseServiceImpl.kt +++ b/core/engine/src/main/java/io/gatehill/imposter/service/script/ScriptedResponseServiceImpl.kt @@ -45,6 +45,7 @@ package io.gatehill.imposter.service.script import com.google.common.cache.CacheBuilder import io.gatehill.imposter.ImposterConfig import io.gatehill.imposter.config.util.EnvVars +import io.gatehill.imposter.expression.helper.RandomHelper import io.gatehill.imposter.http.HttpExchange import io.gatehill.imposter.http.HttpRouter import io.gatehill.imposter.lifecycle.EngineLifecycleHooks @@ -251,9 +252,12 @@ class ScriptedResponseServiceImpl @Inject constructor( scriptEngineName: String, executionContext: ExecutionContext ): Map { + val additionalBindings = mutableMapOf( + "random" to RandomHelper + ) + // fire pre-context build hooks if (!scriptLifecycle.isEmpty) { - val additionalBindings = mutableMapOf() scriptLifecycle.forEach { listener -> listener.beforeBuildingRuntimeContext( httpExchange, @@ -262,11 +266,8 @@ class ScriptedResponseServiceImpl @Inject constructor( executionContext ) } - if (additionalBindings.isNotEmpty()) { - return additionalBindings - } } - return emptyMap() + return additionalBindings } companion object { diff --git a/core/expression/src/main/java/io/gatehill/imposter/expression/eval/RandomEvaluator.kt b/core/expression/src/main/java/io/gatehill/imposter/expression/eval/RandomEvaluator.kt index 4703b60c0..03d403f2d 100644 --- a/core/expression/src/main/java/io/gatehill/imposter/expression/eval/RandomEvaluator.kt +++ b/core/expression/src/main/java/io/gatehill/imposter/expression/eval/RandomEvaluator.kt @@ -43,17 +43,13 @@ package io.gatehill.imposter.expression.eval +import io.gatehill.imposter.expression.helper.RandomHelper import io.gatehill.imposter.util.splitOnCommaAndTrim import org.apache.logging.log4j.LogManager -import java.util.UUID object RandomEvaluator : ExpressionEvaluator { override val name = "random" - val alphabetUpper = ('A'..'Z') - val alphabetLower = ('a'..'z') - val numbers = ('0'..'9') - private val LOGGER = LogManager.getLogger(RandomEvaluator::class.java) override fun eval(expression: String, context: Map): String? { @@ -92,27 +88,16 @@ object RandomEvaluator : ExpressionEvaluator { it.substring(1, it.length - 1) } - val random = when (type) { - "alphabetic" -> getRandomString(length, alphabetUpper + alphabetLower) - "alphanumeric" -> getRandomString(length, alphabetUpper + alphabetLower + numbers) - "any" -> { - if (chars == null) { - LOGGER.warn("chars string must be provided for random type 'any'") - return null - } - getRandomString(length, chars.toList()) - } - "numeric" -> getRandomString(length, numbers.toList()) - "uuid" -> UUID.randomUUID().toString() + return when (type) { + "alphabetic" -> RandomHelper.alphabetic(length, uppercase) + "alphanumeric" -> RandomHelper.alphanumeric(length, uppercase) + "any" -> RandomHelper.any(length, uppercase, chars) + "numeric" -> RandomHelper.numeric(length, uppercase) + "uuid" -> RandomHelper.uuid(uppercase) else -> { LOGGER.warn("Could not parse random expression: $randomConfig") - return null + null } } - return if (uppercase) random.uppercase() else random } - - private fun getRandomString(length: Int, allowedChars: List) = (1..length) - .map { allowedChars.random() } - .joinToString("") } diff --git a/core/expression/src/main/java/io/gatehill/imposter/expression/helper/RandomHelper.kt b/core/expression/src/main/java/io/gatehill/imposter/expression/helper/RandomHelper.kt new file mode 100644 index 000000000..3b03f6192 --- /dev/null +++ b/core/expression/src/main/java/io/gatehill/imposter/expression/helper/RandomHelper.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024-2024. + * + * This file is part of Imposter. + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, as + * defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of rights + * under the License will not include, and the License does not grant to + * you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all of + * the rights granted to you under the License to provide to third parties, + * for a fee or other consideration (including without limitation fees for + * hosting or consulting/support services related to the Software), a + * product or service whose value derives, entirely or substantially, from + * the functionality of the Software. Any license notice or attribution + * required by the License must also include this Commons Clause License + * Condition notice. + * + * Software: Imposter + * + * License: GNU Lesser General Public License version 3 + * + * Licensor: Peter Cornish + * + * Imposter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Imposter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Imposter. If not, see . + */ + +package io.gatehill.imposter.expression.helper + +import org.apache.logging.log4j.LogManager +import java.util.* + +/** + * Generates random values in different formats. + */ +object RandomHelper { + private val logger = LogManager.getLogger(RandomHelper::class.java) + + internal val alphabetUpper = ('A'..'Z') + internal val alphabetLower = ('a'..'z') + internal val numbers = ('0'..'9') + + fun alphabetic(length: Int, uppercase: Boolean): String { + return applyCase(uppercase, getRandomString(length, alphabetUpper + alphabetLower)) + } + + fun alphanumeric(length: Int, uppercase: Boolean): String { + return applyCase(uppercase, getRandomString(length, alphabetUpper + alphabetLower + numbers)) + } + + fun any(length: Int, uppercase: Boolean, chars: String?): String? { + if (chars == null) { + logger.warn("chars string must be provided for random type 'any'") + return null + } + return applyCase(uppercase, getRandomString(length, chars.toList())) + } + + fun numeric(length: Int, uppercase: Boolean): String { + return applyCase(uppercase, getRandomString(length, numbers.toList())) + } + + fun uuid(uppercase: Boolean): String { + return applyCase(uppercase, UUID.randomUUID().toString()) + } + + private fun applyCase(uppercase: Boolean, value: String): String = + if (uppercase) value.uppercase() else value + + private fun getRandomString(length: Int, allowedChars: List) = (1..length) + .map { allowedChars.random() } + .joinToString("") +}