How to include ValidationError.CustomState in ResponseStatus.Meta? - servicestack

I have the following code in one of my validators:
RuleFor(foo => foo)
.Must(foo => foo != null)
.WithState(bar => new { Baz, Qux });
Then in my service I have this:
var validationResult = new FooValidator().Validate(req.Foo);
if (!validationResult.IsValid)
{
throw validationResult.ToException();
}
I thought the CustomState of the ValidationError would be included in the ResponseStatus.Meta property. However, that was not the case.
Is there a clean way I can make this happen?

Previously ServiceStack only supported maintaining a String Dictionary state which would be serialized under the ResponseStatus Error Field Meta Dictionary, e.g:
RuleFor(foo => foo)
.Must(foo => foo != null)
.WithState(bar => new Dictionary<string,string> { ["Baz"] = "Qux" });
Which will be serialized in the Error Field Meta dictionary, e.g:
response.Errors[0].Meta //= ["Bar"] = "Qux"
This is the preferred approach as the same Dictionary<string,string> configured in your CustomState will be serialized as-is in the Meta dictionary.
Otherwise I've just added support for anonymous objects so you're now able to use:
RuleFor(foo => foo)
.Must(foo => foo != null)
.WithState(bar => new { Baz = "Qux" });
Which will result in the same serialized response DTO.
This change is available from v5.9.3+ that's now available on MyGet.

Related

Property 'id' does not exist on type 'string | JwtPayload' [duplicate]

This is a situation I have ran into a couple of times, it seems like it should be fairly straightforward, but I can't find a solution that doesn't set the type to any
A function takes one of two different objects as the argument, checks which object has been received, and returns the corresponding field.
This is a simplified version of the problem, but the issue is that the two objects are only distinguishable by their properties(which have no overlap), and I can't access any of the properties, because they're not present on the other type.
type Obj1 = {
message: string
}
type Obj2 = {
text: string
}
const getText = (obj: Obj1 |obj2): string => {
if (obj.message) {
return obj.message
}
return obj.text
}
You have to narrow down the type. You can do so by using the in operator.
const getText = (obj: Obj1 | Obj2): string => {
if ("message" in obj) {
return obj.message
}
return obj.text
}
You can cast the object to either Obj1 or Obj2:
type Obj1 = {
message: string
}
type Obj2 = {
text: string
}
const getText = (obj: Obj1 | Obj2): string => {
if ((obj as Obj1).message) {
return (obj as Obj1).message
}
return (obj as Obj2).text
}
The real answer to this problem according to what the question owner asked is this
But there might be a time you are using your defined type with primitive type in this way the above solution is not going to work as the problem I faced
here is the situation
type Obj1 = {
message: string
}
const getText = (obj: Obj1 |string): string => {
if (obj.message) {
return obj.message
}
return obj.text
}
so this scenario the solution stated above would not be perfect for you, so you might need to use typeof ✌️
const getText = (obj: Obj1 | string): string => {
if (typeof obj !== 'string') {
return obj.message
}
return obj.text
}
I recommend typescript-is.
import { is } from 'typescript-is';
...
const getText = (obj: Obj1 | Obj2): string => {
if (is<Obj1>(obj)) {
return obj1.message;
}
return obj2.text;
};

Mock `TableClient.QueryAsync()` for Unit Test

I have the following method that I need to test:
public async Task<SomeClass> GetAsync(string partitionKey, string rowKey)
{
var entities = new List<SomeClass>();
await foreach (var e in _tableClient.QueryAsync<SomeClass>(x => x.PartitionKey == partitionKey && x.RowKey == rowKey))
{
entities.Add(e);
}
return entities.FirstOrDefault();
}
I'd like to setup the _tableClient.QueryAsync() (in the moq) to be able to return different result based on the input parameter. This is important to ensure my unit test covers the logic.
My attempt is:
var thingsToMock = new List<(string PartitionKey, string RowKey, string Value)>() {
("maxCount", "maxCount", "0"),
("maxCount", "xyz", "1000"),
("maxCount", "abc", "2000")
};
var tableClientMock = new Mock<TableClient>();
foreach (var thingToMock in thingsToMock)
{
var returnPage = Page<SomeClass>.FromValues(new List<SomeClass>
{
new SomeClass{ PartitionKey = thingToMock.PartitionKey, RowKey = thingToMock.RowKey, Value = thingToMock.Value }
}, null, new Mock<Response>().Object);
var returnPages = AsyncPageable<SomeClass>.FromPages(new[] { returnPage });
Expression<Func<SomeClass, bool>> exp = (x) => x.PartitionKey == thingToMock.PartitionKey && x.RowKey == thingToMock.RowKey ? true : false;
tableClientMock
.Setup(i => i.QueryAsync<SomeClass>(It.Is<Expression<Func<SomeClass, bool>>>(expression => LambdaExpression.Equals(expression, exp)), null, null, default))
.Returns(returnPages);
}
The issue is that the _tableClientMock doesn't seem to return what I expected when I call GetAsync("maxCount", "abc"). I'd expect with this call, it would pass in the same parameters to tableClient.QueryAsync() method, which in my Mock should return instance of SomeClass with value of 2000. But instead, it throw "Object reference not set to an instance of an object." error.
If I change the tableClientMock setup for QueryAsync to be the following, it somewhat works:
.Setup(i => i.QueryAsync<SomeClass>(It.IsAny<Expression<Func<SomeClass, bool>>>(), null, null, default))
But this will not achieve my objective, which is to be able to pass different parameters (partitionKey and rowKey) to get different result.
I'm using the following NuGet package:
"Azure.Data.Tables" Version="12.7.1"
"moq" Version="4.14.1"

Firestore doesn't support JavaScript objects with custom prototypes?

I'm using the node Bigquery Package, to run a simple job. Looking at the results (say data) of the job the effective_date attribute look like this:
effective_date: BigQueryDate { value: '2015-10-02' }
which is obviously an object within the returned data object.
Importing the returned json into Firestore gives the following error:
UnhandledPromiseRejectionWarning: Error: Argument "data" is not a
valid Document. Couldn't serialize object of type "BigQueryDate".
Firestore doesn't support JavaScript objects with custom prototypes
(i.e. objects that were created via the 'new' operator).
Is there an elegant way to handle this? Does one need to iterate through the results and convert / remove all Objects?
The firestore Node.js client do not support serialization of custom classes.
You will find more explanation in this issue:
https://github.com/googleapis/nodejs-firestore/issues/143
"We explicitly decided to not support serialization of custom classes for the Web and Node.JS client"
A solution is to convert the nested object to a plain object. For example by using lodash or JSON.stringify.
firestore.collection('collectionName')
.doc('id')
.set(JSON.parse(JSON.stringify(myCustomObject)));
Here is a related post:
Firestore: Add Custom Object to db
Another way is less resource consuming:
firestore
.collection('collectionName')
.doc('id')
.set(Object.assign({}, myCustomObject));
Note: it works only for objects without nested objects.
Also you may use class-transformer and it's classToPlain() along with exposeUnsetFields option to omit undefined values.
npm install class-transformer
or
yarn add class-transformer
import {classToPlain} from 'class-transformer';
firestore
.collection('collectionName')
.doc('id')
.set(instanceToPlain(myCustomObject, {exposeUnsetFields: false}));
If you have a FirebaseFirestore.Timestamp object then don't use JSON.parse(JSON.stringify(obj)) or classToPlain(obj) as those will corrupt it while storing to Firestore.
It's better to use {...obj} method.
firestore
.collection('collectionName')
.doc('id')
.set({...obj});
Note: do not use new operator for any nested objects inside document class, it'll not work. Instead, create an interface or type for nested object properties like this:
interface Profile {
firstName: string;
lastName: string;
}
class User {
id = "";
isPaid = false;
profile: Profile = {
firstName: "",
lastName: "",
};
}
const user = new User();
user.profile.firstName = "gorv";
await firestore.collection("users").add({...user});
And if you really wanna store class object consists of deeply nested more class objects then use this function to first convert it to plain object while preserving FirebaseFirestore.Timestamp methods.
const toPlainFirestoreObject = (o: any): any => {
if (o && typeof o === "object" && !Array.isArray(o) && !isFirestoreTimestamp(o)) {
return {
...Object.keys(o).reduce(
(a: any, c: any) => ((a[c] = toPlainFirestoreObject(o[c])), a),
{}
),
};
}
return o;
};
function isFirestoreTimestamp(o: any): boolean {
if (o &&
Object.getPrototypeOf(o).toMillis &&
Object.getPrototypeOf(o).constructor.name === "Timestamp"
) {
return true;
}
return false;
}
const user = new User();
user.profile = new Profile();
user.profile.address = new Address();
await firestore.collection("users").add(toPlainFirestoreObject(user));
Serializes a value to a valid Firestore Document data, including object and its childs and Array and its items
export function serializeFS(value) {
const isDate = (value) => {
if(value instanceof Date || value instanceof firestore.Timestamp){
return true;
}
try {
if(value.toDate() instanceof Date){
return true;
}
} catch (e){}
return false;
};
if(value == null){
return null;
}
if(
typeof value == "boolean" ||
typeof value == "bigint" ||
typeof value == "string" ||
typeof value == "symbol" ||
typeof value == "number" ||
isDate(value) ||
value instanceof firestore.FieldValue
) {
return value;
}
if(Array.isArray(value)){
return (value as Array<any>).map((v) => serializeFS(v));
}
const res = {};
for(const key of Object.keys(value)){
res[key] = serializeFS(value[key]);
}
return res;
}
Usage:
await db().collection('products').doc()
.set(serializeFS(
new ProductEntity('something', 123, FieldValue.serverTimestamp()
)));

Realm search in async function and use the result in main thread

I need to perform a search to one of my Realm table based on a keyword. The search function is done in an async function but it is quite slow: 3-5 seconds to search and show the results for 9000 data.
The search is done in three steps:
Search based on the query and return the list of Guid
Return the list of objects based on list of Guid
Use the result value to update the UI
When I try to return List<IssueTable> directly from SearchIssueInProject, I can't access its property later because "this realm instance has been closed" (something like that).
This is my search functions:
public async Task<List<IssueTable>> GetFilteredIssuesInProject(string query)
{
if (!string.IsNullOrEmpty(query))
{
var searchResults = await Task.Run(() => SearchIssueInProject(query));
return searchResults.Select(i => RealmConnection.Find<IssueTable>(i)).ToList();
}
return this.AllIssuesInProject;
}
List<string> SearchIssueInProject(string query)
{
using (var realm = Realm.GetInstance(RealmConfiguration))
{
Func<IssueTable, bool> searchIssue = d =>
string.IsNullOrEmpty(query) ? true :
Contains(d.Id.ToString(), query) ||
Contains(d.Status.DisplayName, query) ||
Contains(d.Status.Value, query) ||
Contains(d.Team.Name, query) ||
Contains(d.Team.Initial, query) ||
Contains(d.StandardIssueCategory.Title, query) ||
Contains(d.StandardIssueType.Title, query) ||
Contains(d.Drawing.Title, query) ||
Contains(d.Location.Title, query) ||
Contains(d.CreatedBy.DisplayName, query) ||
Contains(d.UpdatedBy.DisplayName, query);
var result = realm.All<IssueTable>().Where(searchIssue)
.OrderByDescending(i => i.UpdatedDate)
.Select(i => i.Guid)
.ToList();
return result;
}
}
public List<IssueTable> GetAllIssues()
{
return RealmConnection.All<IssueTable>()
.OrderByDescending(i => i.UpdatedDate)
.ToList();
}
Contains function:
public static bool Contains(string source, string filter)
{
return source.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
}
And this is how I use the search function:
this.WhenAnyValue(x => x.ViewModel.IssueSearchQuery)
.Throttle(TimeSpan.FromMilliseconds(1000), RxApp.MainThreadScheduler)
.DistinctUntilChanged()
.Do(_ =>
{
this.IssueAdapter.Issues.Clear();
})
.Select(searchTerm =>
{
if (SearchingProgressDialog == null && Activity != null)
{
ShowLoadingProgress();
}
var result = this.ViewModel.GetFilteredIssuesInProject(searchTerm);
return result
.ToObservable()
.ObserveOn(RxApp.MainThreadScheduler);
})
.Switch()
.Subscribe(searchResult =>
{
this.IssueAdapter.Issues.AddRange(searchResult);
this.IssueAdapter.NotifyDataSetChanged();
if (SearchingProgressDialog != null)
{
SearchingProgressDialog.Dismiss();
SearchingProgressDialog = null;
}
});
How to improve the search function?
Because you're creating a function and not an expression, the query isn't evaluated by Realm, but rather by LINQ to objects which means every object needs to be fetched by Realm, then the accessed properties will be fetched one by one to be used by the comparison. Unfortunately, all the fields you search by need traversing a related object which is not yet supported by Realm .NET.
One approach to resolve this would be to denormalize your data and copy these properties on the IssueTable object. Then you can execute a query like:
var result = realm.All<IssueTable>()
// Note that the .Where overload we select uses Expression<Func<>>
// Also, StatusDisplayName is a copy of Status.DisplayName
.Where(i => i.StatusDisplayName || ...)
.OrderByDescending(i => i.UpdatedDate)
.ToArray()
.Select(i => i.Guid)
.ToList();

Moq not matching (simple) setup

I'm new to Moq, and having what seems to be a silly problem.
If I perform the setup based on a loop it iwll not match, but if I do the "identicatal" setup by hand I do get a match.
I am using Moq 4.0.10827, from NuGet
My interface being mocked is simple:
public interface IMyInterface
{
string GetValue(string input);
}
The test-entire program is below.
The expected output is identical for both methods, but "Foo" is not printed for Version2()
Code:
class Program
{
static void Main(string[] args)
{
Version1();
Console.WriteLine("---------");
Version2();
Console.WriteLine("---------");
Console.ReadKey();
}
private static void Version1()
{
var mock = new Mock<IMyInterface>();
mock.Setup(x => x.GetValue(It.Is<string>(s => s == "Foo"))).Returns("Foo");
mock.Setup(x => x.GetValue(It.Is<string>(s => s == "Bar"))).Returns("Bar");
IMyInterface obj = mock.Object;
Console.WriteLine(obj.GetValue("Foo"));
Console.WriteLine(obj.GetValue("Bar"));
}
private static void Version2()
{
var mock = new Mock<IMyInterface>();
string[] keys = new string[] { "Foo", "Bar" };
foreach (string key in keys)
{
mock.Setup(x => x.GetValue(It.Is<string>(s => s == key))).Returns(key);
}
IMyInterface obj = mock.Object;
Console.WriteLine(obj.GetValue("Foo")); // Does not match anything
Console.WriteLine(obj.GetValue("Bar"));
}
}
I take it I am missing something.. but what ?
Program output:
Foo
Bar
---------
Bar
---------
Edit: Output from program
Here's a more generic way, this setup alone will let you return what you get from the parameter.
mock.Setup(item => item.GetValue(It.IsAny<string>())).Returns((string input) => input);
By using It.Is<string>(s => s == "Bar") you are probably overwriting first predicate. Try to change the order or string and check it behaves that way.
If you want to check values seperetely, you can do something like this
mock.Setup(item => item.GetValue("Foo")).Returns("Foo");
mock.Setup(item => item.GetValue("Bar")).Returns("Bar");
In a loop:
foreach (string key in keys)
{
mock.Setup(x => x.GetValue(key)).Returns(key);
}
#Ufuk is correct. To clarify, this has nothing to do with Moq. It's the classic "Access to modified closure" issue (that's the warning message ReSharper gives for it).
For example:
void Main()
{
var actions = new List<Func<string, bool>>();
string[] keys = new string[] { "Foo", "Bar" };
foreach (string key in keys)
{
actions.Add(s => s == key);
}
foreach (var action in actions)
{
Console.WriteLine("Is Foo: " + action("Foo"));
Console.WriteLine("Is Bar: " + action("Bar"));
Console.WriteLine();
}
}
Results:
Is Foo: False
Is Bar: True
Is Foo: False
Is Bar: True
See Jon Skeet's answer to C# Captured Variable In Loop and Eric Lippert's Closing over the loop variable considered harmful.

Resources