AutoPopulate attribute not working on AutoQuery DTO - servicestack

I am trying to get the new AutoPopulate attribute to work but I am having some difficulty understanding the new AutoQuery functionality.
To test it out I am aiming to replace this service that is a standard AutoQuery endpoint but it also filters by the logged in users ID. I want to replace it so it works completely with just the model definition.
public class DevExtremeService : ServiceBase
{
public IAutoQueryDb AutoQuery { get; set; }
public QueryResponse<DeWatchedUrlResponse> Any(WatchedUrlDevExRequest request)
{
var q = AutoQuery.CreateDevXQuery(request, Request.GetRequestParams(), Request);
q.Where(x => x.UserAuthCustomId == GetUserId());
var response = AutoQuery.Execute(request, q, base.Request);
return response;
}
}
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
So I deleted the service and updated model to:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
My understanding from reading the release notes is that userAuthId is a variable declared in the AutoQuery #script context that is added by default.
I have tried a few different variations and I cannot get the property to populate. The docs seem focused on audit history and multitenancy but really I am just looking for a quick way to make endpoints.
I have 2 main questions:
Why is the auto populate not working on this property?
Where can I see the default #script definition so I can see how things like userAuthId are defined and better get an understanding how to add my own?
edit
I re-read docs and I gues this only works when writing data to db. I really like the concept of being able to apply #script to a request model via attribute. Is that possible?

AutoQuery CRUD's [AutoPopulate] attribute initially only populated AutoQuery CRUD's Data Model when performing CRUD operations, e.g. Inserting, Updating or Deleting entities.
For ensuring a query only returns a users records, it's recommended to use an AutoFilter instead, which behaves as expected ensuring the query is always applied to the Data Model, e.g:
[ValidateIsAuthenticated]
[Route("/de/watched-urls")]
[AutoFilter(QueryTerm.Ensure, nameof(WatchedUrlRecord.UserAuthCustomId),
Eval = "userAuthId")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
}
However as I can see it's a useful feature I've also just added support for [AutoPopulate] & [AutoMap] attributes on Query DTOs in this commit where your AutoQuery DTO would work as expected where it populates the Request DTO property:
[ValidateIsAuthenticated]
[AutoPopulate(nameof(WatchedUrlDevExRequest.UserAuthCustomId), Eval = "userAuthId")]
[Route("/de/watched-urls")]
public class WatchedUrlDevExRequest : QueryDb<WatchedUrlRecord, DeWatchedUrlResponse>
{
public long UserAuthCustomId { get; set; }
}
This change is available from v5.10.3 that's now available on MyGet.
An alternative approach to populate AutoQuery's Request DTO you could have a custom AutoQuery implementation like you have, an Extensible Query Filter or custom base class or I'd personally go with a Global Request Filter that updates all Request DTOs with a shared interface, e.g:
GlobalRequestFilters.Add((req, res, dto) => {
if (dto is IHasUserAuthCustomId authDto)
{
var session = req.GetSession();
if (session.IsAuthenticated)
authDto.UserAuthCustomId = session.UserAuthId;
}
});
Or you could wrap this logic in a Request Filter Attribute and apply the behavior to Request DTOs that way.
Note: userAuthId is a ServiceStack #Script method that returns the currently authenticated User Id.

Related

TableController and Odata Query

In a small Azure Mobile App, I have the following GET method in a tablecontroller:
public IQueryable<User> GetAllUser()
{
return Query();
}
using the following REST call, I can query users with the lastname='Tiger'
GET: ~/userinfo?$filter=lastName%20eq%20'Tiger'
Now I'd like to add an organisation fields to my user, so I've changed the get method to:
public IQueryable<UserDto> GetAllUser()
{
return Query().Select(u => new UserDto{FirstName=u.FirstName, LastName=u.LastName, Organisation="Acme"});
}
but now, when I try to query my users, using the same filter:
GET: ~/userinfo?$filter=lastName%20eq%20'Tiger'
I get an 'Bad Request' error....
How can I make this work...
Any help would be greatly appreciated....
Try:
public IQueryable<UserDto> GetAll()
{
return Query().AsEnumerable().Select(u =>
new UserDto
{
FirstName = u.FirstName,
LastName = u.LastName,
Organisation = "Acme"
}).AsQueryable();
}
Your ODATA query will be used on the DTO and not the entity.
Your Controller should still use TableController<User>.
You could also use AutoMapper and simply do:
return Query().ProjectTo<UserDTO>();
This will do LINQ to Entities.
Your post helped me solve my own issue!
From what I can tell, it doesn't work because when you're using DTO objects and MappedEntityDomainManager, this.Query() call crashes. In my case, both my Model and DTO object inherit from Azure.Mobile.Server.EntityData.
Basically... avoid this.Query() with MappedEntityDomainManager
Try this:
public IQueryable<UserDto> GetAllUser()
{
return _context.User.Select(u => new UserDto{FirstName=u.FirstName, LastName=u.LastName, Organisation="Acme"});
}
where _context is your DBContext and .User is your User DBSet

Document not available in query direct after store

I'm trying to store a "Role" object and then get a list of Roles, as shown here:
public class Role
{
public Guid RoleId { get; set; }
public string RoleName { get; set; }
public string RoleDescription { get; set; }
}
//Function store:
private void StoreRole(Role role)
{
using (var docSession = docStore.OpenSession())
{
docSession.Store(role);
docSession.SaveChanges();
}
}
// then it return and a function calls this
public List<Role> GetRoles()
{
using (var docSession = docStore.OpenSession())
{
var Roles = from roles in docSession.Query<Role>() select roles;
return Roles.ToList();
}
}
However, in the GetRoles I am missing the last inserted record/document. If I wait 200ms and then call this function the item is there.
So I am not in sync. ?!
How can I solve this, or alternately how could I know when the result is in the document store for querying?
I've used transactions, but cannot figure this out. Update and delete are just fine, but when inserting I need to delay my 'List' call.
You are treating RavenDB as if it is a relational database, and it isn't. Load and Store are ACID operations in RavenDB, Query is not. Indexes (necessary for queries) are updated asynchronously, and in fact, temporary indexes may have to be built from scratch when you do a session.Query<T>() without a durable index specified. So, if you are trying to query for information you JUST stored, or if you are doing the FIRST query that requires a temporary index to be created, you probably won't get the data you expect.
There are methods of customizing your query to wait for non-stale results but you shouldn't lean on these too much because they're indicative of a bad design - it is better to figure out a better way to do the same thing in a way that embraces eventual consistency, either changing your model (so you get consistency via Load/Store - perhaps you could have one document that defines ALL of the roles in a list?) or by changing the application flow so you don't need to Store and then immediately Query.
An additional way of solving this is to query the index with WaitForNonStaleResultsAsOfLastWrite() turned on inside the save function. That way when the save is completed the index will be updated to at least include the change you just made.
You can read more about this here

ServiceStack Json Serializer ignore properties

I have a business requirement to only send permissioned properties in our response payload. For instance, our response DTO may have several properties, and one of them is SSN. If the user doesn't have permissions to view the SSN then I would never want it to be in the Json response. The second requirement is that we send null values if the client has permissions to view or change the property. Because of the second requirement setting the properties that the user cannot view to null will not work. I have to still return null values.
I have a solution that will work. I create an expandoObject by reflecting through my DTO and add only the properties that I need. This is working in my tests.
I have looked at implementing ITextSerializer. I could use that and wrap my response DTO in another object that would have a list of properties to skip. Then I could roll my own SerializeToString() and SerializeToStream(). I don't really see any other ways at this point. I can't use the JsConfig and make a SerializeFn because the properties to skip would change with each request.
So I think that implementing ITextSerializer is a good option. Are there any good examples of this getting implemented? I would really like to use all the hard work that was already done in the serializer and take advantage of the great performance. I think that in an ideal world I would just need to add a check in the WriteType.WriteProperties() to look and the property is one to write, but that is internal and really, most of them are so I can't really take advantage of them.
If someone has some insight please let me know! Maybe I am making the implementation of ITextSerialzer a lot harder that it really is?
Thanks!
Pull request #359 added the property "ExcludePropertyReference" to the JsConfig and the JsConfigScope. You can now exclude references in scope like I needed to.
I would be hesitant to write my own Serializer. I would try to find solutions that you can plug in into the existing ServiceStack code. That way you will have to worry less about updating dlls and breaking changes.
One potential solution would be decorating your properties with a Custom Attributes that you could reflect upon and obscure the property values. This could be done in the Service before Serialization even happens. This would still include values that they user does not have permission to see but I would argue that if you null those properties out they won't even be serialized by JSON anyways. If you keep all the properties the same they you will keep the benefits of strong typed DTOs.
Here is some hacky code I quickly came up with to demonstrate this. I would move this into a plugin and make the reflection faster with some sort of property caching but I think you will get the idea.
Hit the url twice using the following routes to see it in action.
/test?role
/test?role=Admin (hack to pretend to be an authenticated request)
[System.AttributeUsage(System.AttributeTargets.Property)]
public class SecureProperty : System.Attribute
{
public string Role {get;set;}
public SecureProperty(string role)
{
Role = role;
}
}
[Route("/test")]
public class Test : IReturn
{
public string Name { get; set; }
[SecureProperty("Admin")]
public string SSN { get; set; }
public string SSN2 { get; set; }
public string Role {get;set;}
}
public class TestService : Service
{
public object Get(Test request)
{
// hack to demo roles.
var usersCurrentRole = request.Role;
var props = typeof(Test).GetProperties()
.Where(
prop => ((SecureProperty[])prop
.GetCustomAttributes(typeof(SecureProperty), false))
.Any(att => att.Role != usersCurrentRole)
);
var t = new Test() {
Name = "Joe",
SSN = "123-45-6789",
SSN2 = "123-45-6789" };
foreach(var p in props) {
p.SetValue(t, "xxx-xx-xxxx", null);
}
return t;
}
}
Require().StartHost("http://localhost:8080/",
configurationBuilder: host => { });
I create this demo in ScriptCS. Check it out.

Extract paging from IQueryable

I'm using a function to allow query composition from Web UI and I would to implement paging functionality which it will be available for dataBound controls such as ObjectDataSource, gridView, etc:
public class MyClass<TEntity> where TEntity : class
{
FakeEntities xxx = new FakeEntities();
public IEnumerable<TEntity> Get(Func<IQueryable<TEntity>, IQueryable<TEntity>> queryExpression)
{
var query = xxx.Set<TEntity>();
return queryExpression(query).ToList();
}
public int Count()
{
// What Can I return?
}
}
// **** USAGE ****
MyClass<User> u = new MyClass<User>();
var all = u.Get(p => p.Where(z => z.Account == "Smith").OrderBy(order => order.IdOther).Skip(1).Take(2));
The above query use Take and Skip function, so can I get real count of my entities? Obviously I must return Query Count without modifying filter expression.
I found this solution: Get count of an IQueryable<T>
However I get targetInvocationException with inner message {"This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code."}
I know my request could be freak-abnormal, because best practice should to impose to move "presentation needs" to some wrap class and that's is what I'll do. So I don't need anymore to get Count entities on my business logic class.
That's just UI concern only.
Thank you the same.

Recommended pattern for REST service which requires HTTP headers?

I am implementing a RESTful service which will be consumed by a Dojo framewwork's RestStore, which will permit binding the service to various widgets like an interactive grid or a type-ahead ajax select.
The RestStore wants to send and receive the HTTP Range and Content-Range headers in order to restrict the results of the queries to particular subrange.
What is the best practice pattern in ServiceStack (new API) for reacting to HTTP headers as part of the service? Normally, the service method like Get doesn't have access to the HTTP headers, unless I have missed an alternate API.
The current way that I see is to implement an attribute like
public class RangeSupporter : Attribute, IHasRequestFilter, IHasResponseFilter
which will parse the headers on request, and write the headers on response. The DTO would then be marked for this filter.
The filter would transfer the values, say 'First' and 'Last' in and out of the DTO. However, to know that the DTO even has such attributes, it would have to have some marker interface like
interface IHasRangeSupport {
int First { set; get; }
int Last { set; get; }
int Total { set; get; }
}
so that the filter can transfer the information into and out of the DTO the with code similar to:
var r = request as IHasRangeSupport;
if (r != null) {
/// Access the DTO attributes for the range parameters
}
This seems like a lot of ceremony, and a very awkward implementation.
Is there a better pattern for accessing the HTTP headers when implementing a REST service?
In my particular use case, supporting non-REST endpoints (like SOAP) are not required. Only the HTTP end-point is important.
What is the best practice pattern in ServiceStack (new API) for reacting to HTTP headers as part of the service?
I believe you can get the headers in your service class...
public class FooService : Service
{
public object Get(Foo reqeust)
{
//get header
var h1 = base.RequestContext.GetHeader("headerName");
//get header differently
var h2 = base.Request.Headers.Get("headerName");
}
}

Resources