Definition of the data is done via instances of type IndexedGenerator.
Possible values for those can be found in the package de.awagen.kolibri.datatypes.collections.generators
(GitHub Link).
These can hold single collections of values or combinations of multiple collections.
By using the right combination of those, all kinds of permutations of values can be composed.
So lets say you had multiple stores (lets say identified by storeIds), and they are classified into certain types,
and you wanted to request certain queries pro store type.
In this case you can create a generator of queries for each store-type, and per store-type a generator of storeIds.
You could create a PermutatingIndexedGenerator
(see below) for each query-per-type-generator and store-per-type generator,
and combine all generators in a OneAfterAnotherIndexedGenerator
. This now combines all stores with the right queries.
The resulting generator of (storeId, query) pairs could now again be combined with any other generator to provide
the needed combinations.
An IndexedGenerator has the following signature:
trait IndexedGenerator[+T] extends KolibriSerializable {
/** partitioning of the overall data, where each partition is a generator itself,
specific for the distinct generator types **/
def partitions: IndexedGenerator[IndexedGenerator[T]]
/** iterator over all elements generated by the generator **/
def iterator: Iterator[T]
/** number of elements generated by the generator **/
def size: Int
/** partial generator between start (inclusive) and end index (exclusive) **/
def getPart(startIndex: Int, endIndex: Int): IndexedGenerator[T]
/** get element specified by given index if index within valid range, otherwise None **/
def get(index: Int): Option[T]
/** new generator generating elements by retrieving elements of this generator and applying
the map function on it **/
def mapGen[B](f: SerializableFunction1[T, B]): IndexedGenerator[B]
}
Takes the number of elements and a function mapping the index to a value.
Helper-function ByFunctionNrLimitedIndexedGenerator.createFromSeq
takes any Seq and
provides a generator generating the elements of the Seq.
Takes a generator of Seq[T] and a batch size and generates partial generators which in turn generate at most the number of Seq[T] elements given by the batch size (the last element generated might contain less).
Takes Seq of IndexedGenerators and a batchByIndex, and generates generators of Seq[T], where each partial generator corresponds to a single value of the generator provided at index given by batchByIndex. The values for the remaining generators are generated by all possible combinations of the values of each generator.
Takes two generators, generates all possible combinations of values of those generators and applies a mapping function to the pair of values to yield the generated values.
Generator that yields for index n the Seq of values made of one value per generator, while for each generator its n-th element is chosen. Thus no permutations here.
Generator that just starts picking elements from the next generator when the requested element exceeds its own elements, e.g just sequentially provides the elements of the distinct generators.
Generator that takes a sequence of generators and acts like a normal OneAfterAnotherIndexedGenerator, e.g will generate the elements of each contained generator sequentially, thus the number of overall elements is the sum of the elements of the single generators. What differs here is the partitions function, which will keep the groups. This partitioning by this generator and still keep logical groups within it, e.g where each generator passed reflects such as logical grouping.
Takes a number of generators of same type and generates all permutations of all the values within the distinct generators, where the values generated by the distinct generators will have the same Seq-index as its generator.
TODO: give some basic example use cases showing the composition.
The structures below extends the ModifierGeneratorProvider trait, providing to basic defs
trait ModifierGeneratorProvider extends KolibriSerializable {
def partitions: IndexedGenerator[IndexedGenerator[Modifier[RequestTemplateBuilder]]]
def modifiers: Seq[IndexedGenerator[Modifier[RequestTemplateBuilder]]]
}
The signature is as follows:
case class RequestPermutation(params: OrderedMultiValues,
headers: OrderedMultiValues,
bodies: Seq[String],
bodyContentType: ContentType = ContentTypes.`application/json`) extends ModifierGeneratorProvider
The definition of MappingModifier can be found in de.awagen.kolibri.base.processing.modifiers.RequestPermutations
.
It simplifies the logical grouping of data by keys and providing the appropriate generators, combining the data
within the group boundaries.
The signature of the MappingModifier is the following:
case class MappingModifier(keyGen: IndexedGenerator[String],
paramsMapper: ParamsMapper,
headersMapper: HeadersMapper,
bodyMapper: BodyMapper) extends ModifierGeneratorProvider
It simply is a combination of a key generator and distinct mappers for parameters, headers and bodies.
Per generated key, each of the mappers is checked for existence of a matching generator
of type Modifier[RequestTemplateBuilder]
. All of those generators for the specific key
are then combined in an PermutatingIndexedGenerator.
Thus this leads to a Seq of generators, where each generator reflects all permutations
(including all varied parameters, headers, bodies) for a single key.
The generator provided by the partitions
supplier just provides each of those per-key
generators in sequence.
The Seq of generators provided by modifiers
supplier contains a single element,
that is a single OneAfterAnotherIndexedGenerator containing the per-key generators in sequence,
that is it will not mix the generators for the single keys, but fully generate the values for a
single key before going to the next. In this manner it generates all values for all keys.