Using an access token until it expires and then getting a fresh one during parallel execution in Karate [duplicate] - karate

I have an application that creates a token once by using karate.callSingle() in my karate-config file.
This token however expires after some time, so I might need to recreate it after some tests.
My plan would be to set the time of creation in a variable that can be shared in subsequent iterations of the karate-config file, so that I can recreate the token if the time difference is big enough.
Is there a way in Karate in which I can set a variable in the karate-config that can be shared in subsequent iterations ?

In the end I followed Peter Thomas' advice and used Java by "caching" properties between features. Here's my implementation :
var tokenRefreshTimeInMinutes = 5;
var myToken = {};
var KarateCache = Java.type('KarateCache');
var lastRefreshTime = KarateCache.get('lastRefreshTime');
if (!lastRefreshTime || differenceInMinutes(new Date(lastRefreshTime), new Date()) >= tokenRefreshTimeInMinutes) {
myToken = karate.call('theFileRefreshingTheToken');
KarateCache.add('lastRefreshTime', new Date().toUTCString());
KarateCache.add('myToken', JSON.stringify(myToken));
} else {
myToken = JSON.parse(KarateCache.get('myToken'));
}
with this simple KarateCache Java class
private static final Map<String, String> KARATE_CACHE = new ConcurrentHashMap<>();
public static void add(String key, String value) {
KARATE_CACHE.put(key, value);
}
public static String get(String key) {
return KARATE_CACHE.get(key);
}

Are you storing the result of callSingle() to a variable? Like:
var tokenResult = karate.callSingle('createToken.feature', config);
If you save the expiration time to a variable expirationTime inside createToken.feature, you can access it in karate-config.js as tokenResult.expirationTime.

Related

How to use configured JSON options when using a custom InputFormatter?

I would like to replace the default SystemTextJsonInputFormatter with a custom input formatter, either as a sub-class of TextInputFormatter, or (preferably) SystemTextJsonInputFormatter; preferably the latter, to be as close as possible to the built-in ASp.NET Core behaviour without having to duplicate code into my class, which would need to be periodically updated as new ASP.NET Core releases make changes to SystemTextJsonInputFormatter.
There is some relevant info here:
Custom formatters in ASP.NET Core Web API
Which provide this example of how to register a custom input formatter:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new VcardInputFormatter());
});
However, I currently also have this customisation of the JSON options, like so:
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers().AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}
opts there is a Microsoft.AspNetCore.Mvc.JsonOptions object.
When using the built-in SystemTextJsonInputFormatter, these options make it into that formatter instance via its constructor (the relevant code appears to be in MvcCoreMvcOptionsSetup).
I would like to use those same JSON options in my custom input formatter, but cannot see how to obtain a JsonOptions at construction time (the options.InputFormatters.Insert(0, new VcardInputFormatter()); line from above.
I will continue to look through ASP.NET Core internals to try and figure out a way, but thought I would ask here in case anyone can offer some guidance.
Thanks.
A somewhat hacky approach is to simply copy the JsonSerializerOptions properties from the existing SystemTextJsonInputFormatter instance provided by ASP.NET Core, like so:
public static class ServiceCollectionExtensions
{
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
services.AddControllers(
opts => {
// Search for an existing instance of SystemTextJsonInputFormatter in the InputFormatters list;
// this is the instance provided as standard by ASP.NET Core.
int idx = FindSystemTextJsonInputFormatter(
opts.InputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter);
// We expect to find the SystemTextJsonInputFormatter.
if (sysTextJsonInputFormatter is null)
throw new InvalidOperationException("No SystemTextJsonInputFormatter is defined.");
// We will now create our own input formatter, and swap it in to substitute the
// SystemTextJsonInputFormatter instance. First we have some objects to setup.
// Copy the json serializer settings from the existing SystemTextJsonInputFormatter.
var jsonOptions = new JsonOptions();
CopyJsonSerializerOptions(
jsonOptions.JsonSerializerOptions,
sysTextJsonInputFormatter.SerializerOptions);
// TODO/FIXME: Research how best to get a logger for our input formatter!
var loggerFactory = LoggerFactory.Create(builder => {
builder.AddConsole();
});
var logger = loggerFactory.CreateLogger<SystemTextJsonInputFormatter>();
// Perform the substitution.
opts.InputFormatters[idx] = new JsonMergePatchInputFormatter(jsonOptions, logger);
})
.AddJsonOptions(opts => {
// Note. some of these settings are defaults, but we ensure they are set as required.
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// Add our custom converters.
opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
});
return services;
}
private static int FindSystemTextJsonInputFormatter(
FormatterCollection<IInputFormatter> inputFormatters,
out SystemTextJsonInputFormatter? sysTextJsonInputFormatter)
{
// Search for an instance of SystemTextJsonInputFormatter, and store it's index in the InputFormatters list.
// Note. We expect only one instance at most, but if there is more then one then we simply take the first instance.
for (int i = 0; i < inputFormatters.Count; i++)
{
if (inputFormatters[i] is SystemTextJsonInputFormatter formatter)
{
sysTextJsonInputFormatter = formatter;
return i;
}
}
// Not found.
sysTextJsonInputFormatter = null;
return -1;
}
private static void CopyJsonSerializerOptions(JsonSerializerOptions targetOptions, JsonSerializerOptions sourceOptions)
{
// Notes. A hacky but necessary approach, because we are given a pre-built JsonSerializerOptions object
// that can't be replaced, therefore we must copy the various setting into it.
targetOptions.AllowTrailingCommas = sourceOptions.AllowTrailingCommas;
targetOptions.DefaultBufferSize = sourceOptions.DefaultBufferSize;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DictionaryKeyPolicy = sourceOptions.DictionaryKeyPolicy;
targetOptions.Encoder = sourceOptions.Encoder;
targetOptions.IgnoreNullValues = sourceOptions.IgnoreNullValues;
targetOptions.IgnoreReadOnlyFields = sourceOptions.IgnoreReadOnlyFields;
targetOptions.IncludeFields = sourceOptions.IncludeFields;
targetOptions.MaxDepth = sourceOptions.MaxDepth;
targetOptions.NumberHandling = sourceOptions.NumberHandling;
targetOptions.PropertyNameCaseInsensitive = sourceOptions.PropertyNameCaseInsensitive;
targetOptions.PropertyNamingPolicy = sourceOptions.PropertyNamingPolicy;
targetOptions.ReadCommentHandling = sourceOptions.ReadCommentHandling;
targetOptions.ReferenceHandler = sourceOptions.ReferenceHandler;
targetOptions.UnknownTypeHandling = sourceOptions.UnknownTypeHandling;
targetOptions.WriteIndented = sourceOptions.WriteIndented;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
// Re-use the same converter instances; this is OK because their current parent
// SystemTextJsonInputFormatter is about to be discarded.
targetOptions.Converters.Clear();
foreach (var jsonConverter in sourceOptions.Converters)
{
targetOptions.Converters.Add(jsonConverter);
}
}
}
I spent some time wading through ASP.NET Core source code and call stacks in an attempt to find a better/cleaner/official way, but ultimately had to give up and go with the above. Please do let me know if there are better solutions.

Programmatically create index

How do I create an index programmatically in RavenDB?
I tried to follow this example.
This is my index creator:
public class MyIndex : Raven.Client.Indexes.AbstractIndexCreationTask<MyEntity>
{
public MyIndex()
{
Map = col => col.Select(c => new
{
code = c.Code,
len = c.Code.Length,
sub = c.Code.Substring(0, 1)
});
}
}
And here is the caller:
var store = new Raven.Client.Document.DocumentStore
{
Url = "http://localhost:8080"
};
store.Initialize();
try
{
using (var session = store.OpenSession("MyDB"))
{
Raven.Client.Indexes.IndexCreation.CreateIndexes(
typeof(MyIndex).Assembly, store);
}
}
finally
{
store.Dispose();
}
The index was created but not in MyDB but in system database.
How to create the index in MyDB? Is the way I create index correct?
Try this:
specify the database name in your store object
var store = new Raven.Client.Document.DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDB"
};
As MED pointed out, you can provide a default database when attaching to the document store. When doing so, you no longer pass the database name to the OpenSession method. This is the easiest way, and if you're working with a single database then it is the best answer (and should be given the credit as the answer to this question).
But if you need to work with multiple databases, and thus can't use that technique, then you can use this helper method.
public static void CreateIndexes(Assembly assembly, IDocumentStore store,
string databaseName)
{
var catalog = new AssemblyCatalog(assembly);
var provider = new CompositionContainer(catalog);
var commands = store.DatabaseCommands.ForDatabase(databaseName);
IndexCreation.CreateIndexes(provider, commands, store.Conventions);
}
Call it the same way you would call the other method, but now you can pass the database name as a parameter.

Mono.CSharp: how do I inject a value/entity *into* a script?

Just came across the latest build of Mono.CSharp and love the promise it offers.
Was able to get the following all worked out:
namespace XAct.Spikes.Duo
{
class Program
{
static void Main(string[] args)
{
CompilerSettings compilerSettings = new CompilerSettings();
compilerSettings.LoadDefaultReferences = true;
Report report = new Report(new Mono.CSharp.ConsoleReportPrinter());
Mono.CSharp.Evaluator e;
e= new Evaluator(compilerSettings, report);
//IMPORTANT:This has to be put before you include references to any assemblies
//our you;ll get a stream of errors:
e.Run("using System;");
//IMPORTANT:You have to reference the assemblies your code references...
//...including this one:
e.Run("using XAct.Spikes.Duo;");
//Go crazy -- although that takes time:
//foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
//{
// e.ReferenceAssembly(assembly);
//}
//More appropriate in most cases:
e.ReferenceAssembly((typeof(A).Assembly));
//Exception due to no semicolon
//e.Run("var a = 1+3");
//Doesn't set anything:
//e.Run("a = 1+3;");
//Works:
//e.ReferenceAssembly(typeof(A).Assembly);
e.Run("var a = 1+3;");
e.Run("A x = new A{Name=\"Joe\"};");
var a = e.Evaluate("a;");
var x = e.Evaluate("x;");
//Not extremely useful:
string check = e.GetVars();
//Note that you have to type it:
Console.WriteLine(((A) x).Name);
e = new Evaluator(compilerSettings, report);
var b = e.Evaluate("a;");
}
}
public class A
{
public string Name { get; set; }
}
}
And that was fun...can create a variable in the script's scope, and export the value.
There's just one last thing to figure out... how can I get a value in (eg, a domain entity that I want to apply a Rule script on), without using a static (am thinking of using this in a web app)?
I've seen the use compiled delegates -- but that was for the previous version of Mono.CSharp, and it doesn't seem to work any longer.
Anybody have a suggestion on how to do this with the current version?
Thanks very much.
References:
* Injecting a variable into the Mono.CSharp.Evaluator (runtime compiling a LINQ query from string)
* http://naveensrinivasan.com/tag/mono/
I know it's almost 9 years later, but I think I found a viable solution to inject local variables. It is using a static variable but can still be used by multiple evaluators without collision.
You can use a static Dictionary<string, object> which holds the reference to be injected. Let's say we are doing all this from within our class CsharpConsole:
public class CsharpConsole {
public static Dictionary<string, object> InjectionRepository {get; set; } = new Dictionary<string, object>();
}
The idea is to temporarily place the value in there with a GUID as key so there won't be any conflict between multiple evaluator instances. To inject do this:
public void InjectLocal(string name, object value, string type=null) {
var id = Guid.NewGuid().ToString();
InjectionRepository[id] = value;
type = type ?? value.GetType().FullName;
// note for generic or nested types value.GetType().FullName won't return a compilable type string, so you have to set the type parameter manually
var success = _evaluator.Run($"var {name} = ({type})MyNamespace.CsharpConsole.InjectionRepository[\"{id}\"];");
// clean it up to avoid memory leak
InjectionRepository.Remove(id);
}
Also for accessing local variables there is a workaround using Reflection so you can have a nice [] accessor with get and set:
public object this[string variable]
{
get
{
FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
var fields = fieldInfo.GetValue(_evaluator) as Dictionary<string, Tuple<FieldSpec, FieldInfo>>;
if (fields != null)
{
if (fields.TryGetValue(variable, out var tuple) && tuple != null)
{
var value = tuple.Item2.GetValue(_evaluator);
return value;
}
}
}
return null;
}
set
{
InjectLocal(variable, value);
}
}
Using this trick, you can even inject delegates and functions that your evaluated code can call from within the script. For instance, I inject a print function which my code can call to ouput something to the gui console window:
public delegate void PrintFunc(params object[] o);
public void puts(params object[] o)
{
// call the OnPrint event to redirect the output to gui console
if (OnPrint!=null)
OnPrint(string.Join("", o.Select(x => (x ?? "null").ToString() + "\n").ToArray()));
}
This puts function can now be easily injected like this:
InjectLocal("puts", (PrintFunc)puts, "CsInterpreter2.PrintFunc");
And just be called from within your scripts:
puts(new object[] { "hello", "world!" });
Note, there is also a native function print but it directly writes to STDOUT and redirecting individual output from multiple console windows is not possible.

Generate test data in Raven DB

I am looking for a preferred and maintainable way of test data generation in Raven DB. Currently, our team does have a way to do it through .NET code. Example is provided.
However, i am looking for different options. Please share.
public void Execute()
{
using (var documentStore = new DocumentStore { ConnectionStringName = "RavenDb" })
{
documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites;
// Override the default key prefix generation strategy of Pascal case to lower case.
documentStore.Conventions.FindTypeTagName = type => DocumentConvention.DefaultTypeTagName(type).ToLower();
documentStore.Initialize();
InitializeData(documentStore);
}
}
Edit: Raven-overflow is really helpful. Thanks for pointing out to the right place.
Try checking out RavenOverflow. In there, I've got a FakeData project that has fake data (both hardcoded AND randomly generated). This can then be used in either my Tests project or the Main Website :)
Here's some sample code...
if (isDataToBeSeeded)
{
HelperUtilities.CreateSeedData(documentStore);
}
....
public static void CreateSeedData(IDocumentStore documentStore)
{
Condition.Requires(documentStore).IsNotNull();
using (IDocumentSession documentSession = documentStore.OpenSession())
{
// First, check to make sure we don't have any data.
var user = documentSession.Load<User>(1);
if (user != null)
{
// ooOooo! we have a user, so it's assumed we actually have some seeded data.
return;
}
// We have no users, so it's assumed we therefore have no data at all.
// So let's fake some up :)
// Users.
ICollection<User> users = FakeUsers.CreateFakeUsers(50);
StoreFakeEntities(users, documentSession);
// Questions.
ICollection<Question> questions = FakeQuestions.CreateFakeQuestions(users.Select(x => x.Id).ToList());
StoreFakeEntities(questions, documentSession);
documentSession.SaveChanges();
// Make sure all our indexes are not stale.
documentStore.WaitForStaleIndexesToComplete();
}
}
....
public static ICollection<Question> CreateFakeQuestions(IList<string> userIds, int numberOfFakeQuestions)
{
.... you get the idea .....
}

How to share variables between feature runs in Karate?

I have an application that creates a token once by using karate.callSingle() in my karate-config file.
This token however expires after some time, so I might need to recreate it after some tests.
My plan would be to set the time of creation in a variable that can be shared in subsequent iterations of the karate-config file, so that I can recreate the token if the time difference is big enough.
Is there a way in Karate in which I can set a variable in the karate-config that can be shared in subsequent iterations ?
In the end I followed Peter Thomas' advice and used Java by "caching" properties between features. Here's my implementation :
var tokenRefreshTimeInMinutes = 5;
var myToken = {};
var KarateCache = Java.type('KarateCache');
var lastRefreshTime = KarateCache.get('lastRefreshTime');
if (!lastRefreshTime || differenceInMinutes(new Date(lastRefreshTime), new Date()) >= tokenRefreshTimeInMinutes) {
myToken = karate.call('theFileRefreshingTheToken');
KarateCache.add('lastRefreshTime', new Date().toUTCString());
KarateCache.add('myToken', JSON.stringify(myToken));
} else {
myToken = JSON.parse(KarateCache.get('myToken'));
}
with this simple KarateCache Java class
private static final Map<String, String> KARATE_CACHE = new ConcurrentHashMap<>();
public static void add(String key, String value) {
KARATE_CACHE.put(key, value);
}
public static String get(String key) {
return KARATE_CACHE.get(key);
}
Are you storing the result of callSingle() to a variable? Like:
var tokenResult = karate.callSingle('createToken.feature', config);
If you save the expiration time to a variable expirationTime inside createToken.feature, you can access it in karate-config.js as tokenResult.expirationTime.