Skip to content

Commit

Permalink
feat: Add support for PactLoader path value expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Francisco González Ibáñez authored and uglyog committed Aug 2, 2022
1 parent 270808a commit b48e37d
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package au.com.dius.pact.provider.junit.loader

import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.provider.junitsupport.loader.PactFolder
import au.com.dius.pact.provider.junitsupport.loader.PactFolderLoader
import kotlin.jvm.JvmClassMappingKt
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties

class PactFolderLoaderSpec extends Specification {

def 'handles the case where the configured directory does not exist'() {
given:
def file = new File('/does/not/exist')

when:
def result = new PactFolderLoader(file).load('provider')

then:
result == []
}

def 'only includes json files'() {
given:
PactFolder annotation = ParticularFolderPactLoaderAnnotation.class.getAnnotation(PactFolder)

when:
def result = new PactFolderLoader(annotation).load('myAwesomeService')

then:
result.size() == 3
}

def 'only includes json files that match the provider name'() {
given:
PactFolder annotation = ParticularFolderPactLoaderAnnotation.class.getAnnotation(PactFolder)

when:
def result = new PactFolderLoader(annotation).load('myAwesomeService2')

then:
result.size() == 1
}

def 'is able to load files from a directory'() {
given:
File tmpDir = File.createTempDir()
tmpDir.deleteOnExit()
File pactFile = new File(tmpDir, 'pact.json')
pactFile.deleteOnExit()
pactFile.text = this.class.classLoader.getResourceAsStream('pacts/contract.json').text

when:
def result = new PactFolderLoader(tmpDir.path).load('myAwesomeService')

then:
result.size() == 1
}

def 'is able to load files from a directory with spaces in the path'() {
given:
def dirWithSpaces = 'dir with spaces!'

when:
def result = new PactFolderLoader(dirWithSpaces).load('myAwesomeService')

then:
result.size() == 1
}

@RestoreSystemProperties
def "resolves path using default resolver (SystemPropertyResolver)"() {
given:
def exprPath = 'pact${valueToBeResolved}'
System.setProperty('valueToBeResolved', "s")
when:
def result = new PactFolderLoader(exprPath).load('myAwesomeService')
then:
result.size() == 3
}
def "resolves path using given resolver"() {
given:
def exprPath = 'pact${valueToBeResolved}'
def valueResolver = [resolveValue: { val -> 's' }] as ValueResolver
when:
def result = new PactFolderLoader(exprPath, null, valueResolver).load('myAwesomeService')
then:
result.size() == 3
}
def "resolves path using given resolver class"() {
given:
def exprPath = 'pact${valueToBeResolved}'
def constantValueResolver = JvmClassMappingKt.getKotlinClass(ConstantValueResolver.class)
when:
def result = new PactFolderLoader(exprPath, constantValueResolver).load('myAwesomeService')
then:
result.size() == 3
}
@RestoreSystemProperties
def "resolves path using minimal annotation (resolver SystemPropertyResolver)"() {
given:
System.setProperty('pactfolder.path', "pacts")
def annotation = MinimalPactLoaderAnnotation.class.getAnnotation(PactFolder.class)
when:
def result = new PactFolderLoader(annotation).load('myAwesomeService')
then:
result.size() == 3
}
@RestoreSystemProperties
def "resolves path using given revolver class via annotation"() {
given:
System.setProperty('pactfolder.path', "pacts")
def annotation = ParticularResolverPactLoaderAnnotation.class.getAnnotation(PactFolder.class)

when:
def result = new PactFolderLoader(annotation).load('myAwesomeService')

then:
result.size() == 3
}

@PactFolder
static class MinimalPactLoaderAnnotation {

}

@PactFolder('pacts')
static class ParticularFolderPactLoaderAnnotation {

}

@PactFolder(value = 'pact${valueToBeResolved}', valueResolver = ConstantValueResolver)
static class ParticularResolverPactLoaderAnnotation {

}

static class ConstantValueResolver implements ValueResolver {

@Override
String resolveValue(@Nullable String property) {
return 's'
}

@Override
String resolveValue(@Nullable String property, @Nullable String s) {
return 's'
}

@Override
boolean propertyDefined(@NotNull String property) {
return true
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package au.com.dius.pact.provider.junitsupport.loader;

import au.com.dius.pact.core.support.expressions.SystemPropertyResolver;
import au.com.dius.pact.core.support.expressions.ValueResolver;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
Expand All @@ -19,5 +21,10 @@
/**
* @return path to subfolder of project resource folder with pact
*/
String value();
String value() default "${pactfolder.path:}";

/**
* Override the default value resolver for resolving the values in the expressions
*/
Class<? extends ValueResolver> valueResolver() default SystemPropertyResolver.class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,44 @@ import au.com.dius.pact.core.model.DefaultPactReader
import au.com.dius.pact.core.model.DirectorySource
import au.com.dius.pact.core.model.Interaction
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.support.expressions.DataType
import au.com.dius.pact.core.support.expressions.ExpressionParser
import au.com.dius.pact.core.support.expressions.SystemPropertyResolver
import au.com.dius.pact.core.support.expressions.ValueResolver
import java.io.File
import java.net.URLDecoder
import kotlin.reflect.KClass

/**
* Out-of-the-box implementation of [PactLoader]
* that loads pacts from either a subfolder of project resource folder or a directory
*/
class PactFolderLoader<I>(private val path: File) : PactLoader where I : Interaction {
private val pactSource: DirectorySource<I> = DirectorySource(path)
class PactFolderLoader<I> : PactLoader where I : Interaction {

constructor(path: String) : this(File(path))
private val path: File
private val pactSource: DirectorySource<I>

constructor(pactFolder: PactFolder) : this(pactFolder.value)
@JvmOverloads
constructor(
path: String,
valueResolverClass: KClass<out ValueResolver>? = null,
valueResolver: ValueResolver? = null
) {
val resolver = setupValueResolver(valueResolver, valueResolverClass)
val interpolatedPath = ExpressionParser.parseExpression(path, DataType.STRING, resolver) as String
this.path = File(interpolatedPath)
this.pactSource = DirectorySource(this.path)
}

constructor(pactFolder: PactFolder) : this(
pactFolder.value,
pactFolder.valueResolver
)

constructor(path: File) {
this.path = path
this.pactSource = DirectorySource(this.path)
}

override fun description() = "Directory(${pactSource.dir})"

Expand Down Expand Up @@ -46,4 +71,25 @@ class PactFolderLoader<I>(private val path: File) : PactLoader where I : Interac
return path
}
}

private fun setupValueResolver(
valueResolver: ValueResolver?,
valueResolverClass: KClass<out ValueResolver>?
): ValueResolver {
var resolver: ValueResolver = valueResolver ?: SystemPropertyResolver
if (valueResolverClass != null) {
if (valueResolverClass.objectInstance != null) {
resolver = valueResolverClass.objectInstance!!
} else {
try {
resolver = valueResolverClass.java.newInstance()
} catch (e: InstantiationException) {
PactBrokerLoader.logger.warn(e) { "Failed to instantiate the value resolver, using the default" }
} catch (e: IllegalAccessException) {
PactBrokerLoader.logger.warn(e) { "Failed to instantiate the value resolver, using the default" }
}
}
}
return resolver
}
}

0 comments on commit b48e37d

Please sign in to comment.