Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get configuration dynamically #37

Merged
merged 14 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/main/kotlin/com/featurevisor/sdk/Conditions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ object Conditions {
attributeValue is AttributeValue.StringValue && conditionValue is ConditionValue.ArrayValue -> {
when (operator) {
IN_ARRAY -> attributeValue.value in conditionValue.values
NOT_IN_ARRAY -> (attributeValue.value in conditionValue.values).not()
NOT_IN_ARRAY -> (attributeValue.value !in conditionValue.values)
else -> false
}
}
Expand Down Expand Up @@ -142,6 +142,10 @@ object Conditions {
}

private fun compareVersions(actual: String, condition: String): Int {
return SemVer.parse(actual).compareTo(SemVer.parse(condition))
return try {
SemVer.parse(actual).compareTo(SemVer.parse(condition))
} catch (e: Exception) {
0
}
}
}
13 changes: 5 additions & 8 deletions src/main/kotlin/com/featurevisor/sdk/Instance+Evaluation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,9 @@ fun FeaturevisorInstance.evaluateVariation(featureKey: FeatureKey, context: Cont
evaluation = Evaluation(
featureKey = featureKey,
reason = ERROR,
error(e)
)

this.logger?.error("error", evaluation.toDictionary())
this.logger?.error(message = e.message.orEmpty(), details = evaluation.toDictionary())

return evaluation
}
Expand Down Expand Up @@ -433,10 +432,9 @@ fun FeaturevisorInstance.evaluateFlag(featureKey: FeatureKey, context: Context =
evaluation = Evaluation(
featureKey = featureKey,
reason = ERROR,
error(e)
)

this.logger?.error("error", evaluation.toDictionary())
this.logger?.error(message = e.message.orEmpty(), details = evaluation.toDictionary())

return evaluation
}
Expand Down Expand Up @@ -475,7 +473,7 @@ fun FeaturevisorInstance.evaluateVariable(

// initial
if (!statuses.ready && initialFeatures?.get(featureKey)?.variables?.get(variableKey) != null) {
val variableValue = initialFeatures?.get(featureKey)?.variables?.get(variableKey)
val variableValue = initialFeatures[featureKey]?.variables?.get(variableKey)
evaluation = Evaluation(
featureKey = featureKey,
reason = INITIAL,
Expand Down Expand Up @@ -640,14 +638,13 @@ fun FeaturevisorInstance.evaluateVariable(
logger?.debug("using default value", evaluation.toDictionary())
return evaluation
}
}catch (e: Exception){
} catch (e: Exception){
evaluation = Evaluation(
featureKey = featureKey,
reason = ERROR,
error(e)
)

this.logger?.error("error", evaluation.toDictionary())
this.logger?.error(message = e.message.orEmpty(), details = evaluation.toDictionary())

return evaluation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ inline fun <reified T : Any> FeaturevisorInstance.getVariableObject(
}
}

inline fun <reified T : Any> FeaturevisorInstance.getVariableJSON(
inline fun <reified T: Any> FeaturevisorInstance.getVariableJSON(
featureKey: FeatureKey,
variableKey: VariableKey,
context: Context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.featurevisor.types.Context
import com.featurevisor.types.FeatureKey
import com.featurevisor.types.VariationValue

internal fun FeaturevisorInstance.getVariation(featureKey: FeatureKey, context: Context): VariationValue? {
fun FeaturevisorInstance.getVariation(featureKey: FeatureKey, context: Context): VariationValue? {
val evaluation = evaluateVariation(featureKey, context)
return when {
evaluation.variationValue != null -> evaluation.variationValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fun benchmarkFeature(option: BenchMarkOptions) {

val datafileBuildStart = System.nanoTime().toDouble()

val datafileContent = buildDataFileForStaging(option.projectRootPath)
val datafileContent = buildDataFileAsPerEnvironment(option.projectRootPath,"staging")

val datafileBuildEnd = System.nanoTime().toDouble()

Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/com/featurevisor/testRunner/CommandExecuter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,15 @@ private fun String.runCommand(workingDir: File): String? =
printMessageInRedColor("Exception while executing command -> ${e.message}")
null
}

fun createCommandForConfiguration()=
"npx featurevisor config --print --pretty"

fun getConfigurationJson(projectRootPath: String) =
try {
createCommandForConfiguration().runCommand(getFileForSpecificPath(projectRootPath))
}catch (e:Exception){
printMessageInRedColor("Exception in createCommandForConfiguration Commandline execution --> ${e.message}")
null
}

4 changes: 4 additions & 0 deletions src/main/kotlin/com/featurevisor/testRunner/Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,7 @@ private fun parseConditionValue(value: Any?): ConditionValue {
}
}

fun parseConfiguration(projectRootPath: String) =
json.decodeFromString(Configuration.serializer(),getConfigurationJson(projectRootPath)!!)


82 changes: 47 additions & 35 deletions src/main/kotlin/com/featurevisor/testRunner/TestExecuter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,60 @@ data class TestProjectOption(
val showDatafile: Boolean = false,
val onlyFailures: Boolean = false,
val fast: Boolean = false,
val testDirPath: String = "tests",
val projectRootPath: String = getRootProjectDir()
val projectRootPath: String? = null
)

fun startTest(option: TestProjectOption) {
fun startTest(option: TestProjectOption) = option.projectRootPath?.let { it ->
val projectConfig = parseConfiguration(it)
var hasError = false
val folder = File("${option.projectRootPath}/${option.testDirPath}")
val folder = File(projectConfig.testsDirectoryPath)
val listOfFiles = folder.listFiles()?.sortedBy { it }
var executionResult: ExecutionResult? = null
var executionResult: ExecutionResult?
val startTime = System.currentTimeMillis()
var passedTestsCount = 0
var failedTestsCount = 0
var passedAssertionsCount = 0
var failedAssertionsCount = 0
val datafileContentByEnvironment: MutableMap<String, DatafileContent> = mutableMapOf()

if (!listOfFiles.isNullOrEmpty()) {
val datafile =
if (option.fast) buildDataFileForBothEnvironments(projectRootPath = option.projectRootPath) else DataFile(
null,
null
if (option.fast) {
for (environment in projectConfig.environments) {
val datafileContent = buildDataFileAsPerEnvironment(
projectRootPath = it,
environment = environment
)
if (option.fast && (datafile.stagingDataFiles == null || datafile.productionDataFiles == null)) {
return
datafileContentByEnvironment[environment] = datafileContent
}
}

if (!listOfFiles.isNullOrEmpty()) {
for (file in listOfFiles) {
if (file.isFile) {
if (file.extension.equals("yml", true)) {
val filePath = file.absoluteFile.path
try {
executionResult = executeTest(filePath, dataFile = datafile, option)
} catch (e: Exception) {
printMessageInRedColor("Exception in $filePath --> ${e.message}")
}

if (executionResult == null) {
continue
}

if (executionResult.passed) {
passedTestsCount++
if (listOfFiles.isNotEmpty()) {
executionResult = try {
executeTest(filePath, datafileContentByEnvironment, option, projectConfig)
} catch (e: Exception) {
printMessageInRedColor("Exception while executing assertion -> ${e.message}")
null
}
if (executionResult == null) {
continue
}

if (executionResult.passed) {
passedTestsCount++
} else {
hasError = true
failedTestsCount++
}

passedAssertionsCount += executionResult.assertionsCount.passed
failedAssertionsCount += executionResult.assertionsCount.failed
} else {
hasError = true
failedTestsCount++
printMessageInRedColor("The file is not valid yml file")
}

passedAssertionsCount += executionResult.assertionsCount.passed
failedAssertionsCount += executionResult.assertionsCount.failed
} else {
printMessageInRedColor("The file is not valid yml file")
}
}
}
Expand All @@ -81,17 +86,23 @@ fun startTest(option: TestProjectOption) {
} else {
printMessageInRedColor("Directory is Empty or not exists")
}
}
} ?: printNormalMessage("Root Project Path Not Found")


private fun executeTest(filePath: String, dataFile: DataFile, option: TestProjectOption): ExecutionResult? {
private fun executeTest(
filePath: String,
datafileContentByEnvironment: MutableMap<String, DatafileContent>,
option: TestProjectOption,
configuration: Configuration
): ExecutionResult? {
val test = parseTestFeatureAssertions(filePath)

val executionResult = ExecutionResult(
passed = true,
assertionsCount = AssertionsCount(0, 0)
)

if (test != null){
if (test != null) {
val key = when (test) {
is Test.Feature -> test.value.key
is Test.Segment -> test.value.key
Expand All @@ -105,14 +116,15 @@ private fun executeTest(filePath: String, dataFile: DataFile, option: TestProjec
is Test.Feature -> {
testFeature(
testFeature = test.value,
dataFile = dataFile,
datafileContentByEnvironment = datafileContentByEnvironment,
option = option
)
}

is Test.Segment -> {
testSegment(
testSegment = test.value,
configuration = configuration,
option = option
)
}
Expand Down
15 changes: 8 additions & 7 deletions src/main/kotlin/com/featurevisor/testRunner/TestFeature.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement

fun testFeature(testFeature: TestFeature, dataFile: DataFile, option: TestProjectOption): TestResult {
fun testFeature(
testFeature: TestFeature,
datafileContentByEnvironment:MutableMap<String, DatafileContent>,
option: TestProjectOption
): TestResult {
val testStartTime = System.currentTimeMillis()
val featureKey = testFeature.key

Expand Down Expand Up @@ -40,15 +44,12 @@ fun testFeature(testFeature: TestFeature, dataFile: DataFile, option: TestProjec
return@forEach
}

val datafileContent = if (option.fast) {
if (it.environment.equals("staging", true)) dataFile.stagingDataFiles else dataFile.productionDataFiles
} else {
getDataFileContent(
val datafileContent = datafileContentByEnvironment[it.environment]
?: getDataFileContent(
featureName = testFeature.key,
environment = it.environment,
projectRootPath = option.projectRootPath
projectRootPath = option.projectRootPath.orEmpty()
)
}

if (option.showDatafile) {
printNormalMessage("")
Expand Down
9 changes: 3 additions & 6 deletions src/main/kotlin/com/featurevisor/testRunner/TestSegment.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.featurevisor.testRunner

import com.featurevisor.sdk.segmentIsMatched
import com.featurevisor.types.TestResult
import com.featurevisor.types.TestResultAssertion
import com.featurevisor.types.TestResultAssertionError
import com.featurevisor.types.TestSegment
import com.featurevisor.types.*

fun testSegment(testSegment: TestSegment, option: TestProjectOption): TestResult {
fun testSegment(testSegment: TestSegment,configuration: Configuration,option: TestProjectOption): TestResult {
val testStartTime = System.currentTimeMillis()
val segmentKey = testSegment.key

Expand Down Expand Up @@ -36,7 +33,7 @@ fun testSegment(testSegment: TestSegment, option: TestProjectOption): TestResult
return@forEach
}

val yamlSegment = parseYamlSegment("${option.projectRootPath}/segments/$segmentKey.yml")
val yamlSegment = parseYamlSegment("${configuration.segmentsDirectoryPath}/$segmentKey.yml")
val expected = it.expectedToMatch
val actual = segmentIsMatched(yamlSegment!!, it.context)
val passed = actual == expected
Expand Down
26 changes: 6 additions & 20 deletions src/main/kotlin/com/featurevisor/testRunner/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.featurevisor.testRunner

import com.featurevisor.sdk.FeaturevisorInstance
import com.featurevisor.sdk.InstanceOptions
import com.featurevisor.sdk.emptyDatafile
import com.featurevisor.types.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -237,29 +238,14 @@ fun checkJsonIsEquals(a: String, b: String): Boolean {
return map1 == map2
}

fun buildDataFileForBothEnvironments(projectRootPath: String): DataFile =
DataFile(
stagingDataFiles = buildDataFileForStaging(projectRootPath),
productionDataFiles = buildDataFileForProduction(projectRootPath)
)

fun buildDataFileForStaging(projectRootPath: String) = try {
getJsonForDataFile(environment = "staging", projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}
} catch (e: Exception) {
printMessageInRedColor("Unable to parse staging data file")
null
}

fun buildDataFileForProduction(projectRootPath: String) = try {
getJsonForDataFile(environment = "production", projectRootPath = projectRootPath)?.run {
fun buildDataFileAsPerEnvironment(projectRootPath: String,environment: String) = try {
getJsonForDataFile(environment = environment, projectRootPath = projectRootPath)?.run {
convertToDataClass<DatafileContent>()
}

} ?: emptyDatafile
} catch (e: Exception) {
printMessageInRedColor("Unable to parse production data file")
null
printMessageInRedColor("Unable to parse data file")
emptyDatafile
}

fun getDataFileContent(featureName: String, environment: String, projectRootPath: String) =
Expand Down
18 changes: 18 additions & 0 deletions src/main/kotlin/com/featurevisor/types/Types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,21 @@ data class DataFile(
val stagingDataFiles: DatafileContent? = null,
val productionDataFiles: DatafileContent? = null
)

@Serializable
data class Configuration(
val environments:List<String>,
val tags: List<String>,
val defaultBucketBy:String,
val prettyState:Boolean,
val prettyDatafile:Boolean,
val stringify:Boolean,
val featuresDirectoryPath:String,
val segmentsDirectoryPath:String,
val attributesDirectoryPath:String,
val groupsDirectoryPath:String,
val testsDirectoryPath:String,
val stateDirectoryPath:String,
val outputDirectoryPath:String,
val siteExportDirectoryPath:String
)
Loading