casting between C# derived classes, in F# - f#

I have two types coming from a C# lib:
one is defined like this (only signatures):
public class CallResult<T>: CallResult
{
public CallResult([AllowNull]T data, Error? error): base(error)
public static implicit operator bool(CallResult<T> obj)
public bool GetResultOrError([MaybeNullWhen(false)] out T data, [NotNullWhen(false)] out Error? error)
public new static WebCallResult<T> CreateErrorResult(Error error)
}
and the second one derives from it:
public class WebCallResult<T>: CallResult<T>
{
public HttpStatusCode? ResponseStatusCode { get; set; }
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? ResponseHeaders { get; set; }
public WebCallResult(HttpStatusCode? code, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, [AllowNull] T data, Error? error): base(data, error)
public WebCallResult(WebCallResult<T> callResult): base(callResult.Data, callResult.Error)
public static WebCallResult<T> CreateFrom<Y>(WebCallResult<Y> source) where Y : T
public static WebCallResult<T> CreateErrorResult(HttpStatusCode? code, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, Error error)
}
and they both come from:
public class CallResult
{
public Error? Error { get; internal set; }
public bool Success => Error == null;
public CallResult(Error? error)
public static implicit operator bool(CallResult obj)
public static WebCallResult CreateErrorResult(Error error)
}
Some api calls return a CallResult, others return a WebCallResult.
Right now I use two times the same code to handle it:
// turn a webcall result into a Result object
let processResultWeb (applyOnOk: 'a -> 'b) (result: WebCallResult<'a>) =
match result.Success with
| true -> Result.Ok (applyOnOk result.Data)
| false -> Result.Error (decodeError result.Error)
// turn a webcall result into a Result object
let processResult (applyOnOk: 'a -> 'b) (result: CallResult<'a>) =
match result.Success with
| true -> Result.Ok (applyOnOk result.Data)
| false -> Result.Error (decodeError result.Error)
Which doesn't really make sense since it's the same code and I'm only caring about the data from the base class (CallResult).
So I would like to cast both types to the base class:
let a: WebCallResult = ...
let r = a :> CallResult
but this results in a compiler error:
[FS0001] The type 'CallResult' is not compatible with the type 'WebCallResult<'a>'
how can I check the result for both types by just accessing the fields from their base class, but using the same generic type.
Edit:
the source code of the classes is here: https://pastebin.com/mrw5W7xk
The issue is that I want to go from:
WebCallResult<'a'> to CallResult<'a>
and the generic seems to be the issue.

Using your code, I'm able to cast with no problem, even with generics. Here's an example:
let foo x =
WebCallResult<_>(System.Nullable(), Array.empty, x, Error())
let a : WebCallResult<int> = foo 3
let r = a :> CallResult<_>
let b : WebCallResult<string> = foo "str"
let q = b :> CallResult<_>

Related

how to get configKey in custom feign decode

I have several APIs defined in a TestFeignClient:
#FeignClient(name = "testFeign", url = "xxx")
public interface TestFeignClient {
#CustomAnnotation(value = 1)
#GetMapping("/test2")
String test1();
#CustomAnnotation(value = 2)
#GetMapping("/test2")
String test2();
}
also I define a custom decoder like this:
public class MyDecoder implements Decoder {
#Override
public Object decode(Response response, Type type) {
// how to get the #CustomAnnotation value in this method ?
return null;
}
}
my question is how can I get the #CustomAnnotation value in the decoder method ??

quarkus mutiny: neo4j type mismatch compilation handling

This is my code:
#Path("/hello")
#AllArgsConstructor
public class GreetingResource {
private final Driver driver;
#GET
#Produces(MediaType.TEXT_PLAIN)
public Uni<String> hello() {
return Multi.createFrom().resource(
driver::rxSession,
session -> session.readTransaction(tx -> {
RxResult result = tx.run("MATCH (f:Fruit) RETURN f.name as name ORDER BY f.name");
return Multi.createFrom().publisher(result.records()).map(record -> record.get("name").asString());
})
).withFinalizer(session -> {
return Multi.createFrom().publisher(session.close());
});
}
}
I'm getting those two compilation messages:
Type mismatch: cannot convert from Multi<Object> to Uni<String>
Type mismatch: cannot convert from Multi<Object> to Uni<Void>
I don't quite figure since record.get("name").asString returns me an String...
Any ideas?
The finalizer function must return a Uni<Void>. In your code, it returns a Publisher<Object>. Also, your method is going to return a Multi and not a Uni.
Try the following approach:
#GET
#Produces(MediaType.TEXT_PLAIN)
public Multi<String> hello() {
return Multi.createFrom().resource(
driver::rxSession,
session -> session.readTransaction(tx -> {
RxResult result = tx.run(
"MATCH (f:Fruit) RETURN f.name as name ORDER BY f.name");
return Multi.createFrom().publisher(result.records())
.map(record -> record.get("name").asString());
})
).withFinalizer(session -> {
return Uni.createFrom().publisher(session.close());
});
}

Map generic type to record using pattern matching

I've a Union type like this:
type AccountOpenened =
{ Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal }
type AccountClosed = { AccountId: Guid }
type AccountEvent = AccountOpenend | AccountClosed
I also have a 3rd party library from which I can get IReadOnlyList<IEvent> whereas IEvent as this signature:
public interface IEvent {
object Data { get; }
}
The library also provides a generic class Event<T> which has this signature:
public class Event<T> : IEvent {
public T Data { get; set; }
}
What I'm trying to achieve is to map an instance of IReadOnlyList<IEvent> to an AccountEvent list using exhaustive pattern matching.
I tried something like this:
let map (input: IEvent): AccountEvent =
match input with
| :? (Event<AccountOpened>) as e ->
{ Owner = e.Data.Owner
AccountId = e.Data.AccountId
CreatedAt = e.Data.CreatedAt
StartingBalance = e.Data.StartingBalance }
| :? (Event<AccountClosed>) as e ->
{ AccountId = e.Data.AccountId }
| _ -> failwith "Unknown Event"
This doesn't work because I get this compiler error:
This expression was expected to have type 'AccountEvent' but here has type 'AccountCreation'
What am I doing wrong here?

How to input null-value into specflow step definition table

How can I input a null value in Specflow through a table?
Let's look at an overly simplistic example:
When a tire is attached to a car
| CarId | TireModel | FabricationDate | Batch |
| 1 | Nokian Hakka R | 2015-09-1 | |
The empty string in the Batch column is interpreted as text by specflow and as such, empty string. Is there a special syntax to mark that column as null?
You can create your own IValueRetriever and replace default one with yours
public class StringValueRetriver : IValueRetriever
{
public bool CanRetrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return propertyType == typeof(string);
}
public object Retrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return string.IsNullOrEmpty(keyValuePair.Value) ? null : keyValuePair.Value;
}
}
Some where in your scenario steps
[BeforeScenario]
public void BeforeScenario()
{
Service.Instance.ValueRetrievers.Unregister<TechTalk.SpecFlow.Assist.ValueRetrievers.StringValueRetriever>();
Service.Instance.ValueRetrievers.Register(new StringValueRetriver());
}
older syntax:
[BeforeScenario]
public void BeforeScenario()
{
var defaultStringValueRetriever = Service.Instance.ValueRetrievers.FirstOrDefault(vr => vr is TechTalk.SpecFlow.Assist.ValueRetrievers.StringValueRetriever);
if (defaultStringValueRetriever != null)
{
Service.Instance.UnregisterValueRetriever(defaultStringValueRetriever);
Service.Instance.RegisterValueRetriever(new StringValueRetriver());
}
}
From SpecFlow 3 on-wards, in your Steps class, you can just put the following code. And in the feature file just put null value like this. Now when you use the CreateSet function then it will be deserialized correctly.
Id | Value
1 | <null>
[Binding]
public static class YourStepClass
{
[BeforeTestRun]
public static void BeforeTestRun()
{
Service.Instance.ValueRetrievers.Register(new NullValueRetriever("<null>"));
}
}
I don't believe there is a special syntax for null and I think you'll have to just handle the conversion yourself. The value retrievers have been revised in the v2 branch and you might be able to handle this by deregistering the standard string value retriever and registering your own implementation which looks for some special syntax and returns null.
In the current 1.9.* version though I think you'll just have to check for empty string and return null yourself.
I've just chosen to do this on a case by case manner using a simple extension method.
In the handler I convert the passed in example value parameter and call NullIfEmpty()
Example usage
AndICheckTheBatchNumber(string batch) {
batch = batch.NullIfEmpty();
//use batch as null how you intended
}
Extension method
using System;
namespace Util.Extensions
{
public static class StringExtensions
{
public static string NullIfEmpty(this string str)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
return str;
}
}
}
Combining answers, I did the following:
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;
using TechTalk.SpecFlow.Assist.ValueRetrievers;
namespace Util.Extensions
{
public class NullValueComparer : IValueComparer
{
private readonly string _nullValue;
public NullValueComparer(string nullValue)
{
_nullValue = nullValue;
}
public bool CanCompare(object actualValue)
{
return actualValue is null || actualValue is string;
}
public bool Compare(string expectedValue, object actualValue)
{
if (_nullValue == expectedValue)
{
return actualValue == null;
}
return expectedValue == (string)actualValue;
}
}
}
And referenced it like this:
[Binding]
public class MyStepDefinitions
{
private MyTestDto _testDto;
private AnotherDtoFromElsewhere _actual;
[BeforeScenario]
public void BeforeTestRun()
{
Service.Instance.ValueRetrievers.Register(new NullValueRetriever("<null>"));
Service.Instance.ValueComparers.Register(new NullValueComparer("<null>"));
}
[When(#"Some test with table:")]
public void WhenTestWithTable(Table table)
{
_testDto = table.CreateInstance<MyTestDto>();
var actual = new AnotherDtoFromElsewhere();
table.CompareToInstance(actual);
}
[Then(#"X should match:")]
public void ThenShouldMatch(Table table)
{
table.CompareToInstance(_actual);
}
}

IDependencyResolver interface implementation in F# Web API

I am re-writing a C# ASP.NET Web API application in F#. I have Models and Controllers done and I moved onto MyDependencyResolver that implements IDependencyResolver.
I am having a problem implementing the GetService method, whose signature in C# is:
object GetService(System.Type serviceType)
So I need to return an obj and take a System.Type as a parameter.
This is what I have so far in F#:
type MyDependencyResolver() =
interface System.Web.Http.Dependencies.IDependencyResolver with
member this.BeginScope() : IDependencyScope =
this :> IDependencyScope
member this.GetService(serviceType:Type) : obj =
if (serviceType = typeof<Controllers.HomeController>) then
let homeController = new Controllers.HomeController(arg1, arg2, arg3, arg4)
homeController :> obj
// ???
elif (serviceType = typeof<_>) then
null
member this.GetServices (serviceType: Type) :IEnumerable<obj> =
let x = new List<obj>()
x :> IEnumerable<obj>
member this.Dispose() =
()
So if serviceType is of type HomeController I want to return an instance of HomeController, and if it's of any other type I want to return null. How do I do that in F#?
Edit:
GetService method in C#:
public object GetService(Type serviceType)
{
if (serviceType == typeof(Controllers.HomeController)){
return new Controllers.HomeController(arg1, arg2, arg3, arg4);
}
return null;
}
You could just use Activator.CreateInstance(serviceType) but where do you get the constructor arguments from?
In my projects I use Unity, which is configured as follows:
let private ConfigureUnity (config : HttpConfiguration) =
let rec unityResolver (container : IUnityContainer) =
{ new IDependencyResolver with
member this.BeginScope() =
unityResolver(container.CreateChildContainer()) :> IDependencyScope
member this.GetService serviceType =
try container.Resolve(serviceType) with
| :? ResolutionFailedException -> null
member this.GetServices serviceType =
try container.ResolveAll(serviceType) with
| :? ResolutionFailedException -> Seq.empty
member this.Dispose() = container.Dispose()
}
config.DependencyResolver <- (new UnityContainer())
.RegisterType<IFoo, FooImplementation>(new HierarchicalLifetimeManager())
.RegisterType<IBar, BarImplementation>(new HierarchicalLifetimeManager())
|> unityResolver
Classes (such as your controllers) are then resolved automatically and Unity will create dependencies (the constructor arguments) for you. Using other dependency injection frameworks should be straightforward.

Resources