ServiceStack group deleting and updating - servicestack

I'm trying to implement ServiceStack group deleting and updating. For group deleting, the endpoint is like ~/item/{ItemIdList}, the ItemIdList is of List<Guid> type. I already wrote the code but I'm not sure what to put into the {ItemIdList} part? For a single item, it is ~/item/AItem'sActualId. What if it is a list?
And is it the correct approach or how to implement group deleting/updating. Thanks.

It shouldn't be on the /path/info info since the url doesn't identify a resource, it's better to be specified on the queryString on the /items resource collection, e.g:
[Route("/items", "DELETE")]
public class DeleteItems
{
public List<Guid> Ids { get; set; }
}
Then call it with:
DELETE /items?Ids={Guid1},{Guid2}
or via a Service Client:
client.Delete(new DeleteItems { Ids = new[] { Guid1, Guid2 }.ToList());
If you want to be able to use ANY HTTP Verb, your custom path can be a command like:
[Route("/items/delete")]
public class DeleteItems
{
public List<Guid> Ids { get; set; }
}
Then handle it using Any in your Service:
public object Any(DeleteItems request) { ... }
Which you can now call with ANY HTTP Method, e.g:
client.Post(new DeleteItems { Ids = new[] { Guid1, Guid2 }.ToList());

Related

Token in permissions dissapears using documentdb and xamarin forms

I am trying to send permissions for documentdb for a specific user from my azure server to my client app, which are xamarin forms.
On server side everything looks good and I can see users specific permissions and token.
But when permissions are received in the client, the token is stripped away, why?
I am new with documentdb so hopefully it is just me.
I am using an Azure Mobile App service as backend.
My backend controller returns an object holding properties for documentdb database including a list of permissions for the user.
public class DbConfig
{
public string DatabaseName { get; set; }
public string CollectionId { get; set; }
public string EndpointUri { get; set; }
public IList<Permission> Permissions { get; set; }
}
I create a permission for a user for the entire collection if not already created.
public async Task<Permission> CreatePermissionAsync(string resourceLink, string userLink, PermissionMode mode, string resourcePartitionKey = null)
{
try
{
Permission permission = new Permission
{
Id = Guid.NewGuid().ToString("N"),
PermissionMode = mode,
ResourceLink = resourceLink
};
if (resourcePartitionKey != null)
{
permission.ResourcePartitionKey = new PartitionKey(resourcePartitionKey);
}
var result = await client.CreatePermissionAsync(userLink, permission);
DbConfig.Permissions.Add(result);
return result;
}
catch (Exception e)
{
Trace.WriteLine($"##### Exception: {e}");
throw;
}
}
I retrieve permissions for a user with this method.
public List<Permission> GetPermissionsForUserPermissionLink(User user)
{
var permFeed = client.CreatePermissionQuery(user.PermissionsLink);
List<Permission> permList = new List<Permission>();
foreach (Permission perm in permFeed)
{
permList.Add(perm);
DbConfig.Permissions.Add(perm);
}
return permList;
}
On the client side in my Xamarin forms app i use this call to my custom controller in the backend.
var parameters = new Dictionary<string, string> { { "userid", Settings.AzureUserId } };
dbConfig = await client.InvokeApiAsync<DbConfig>("Settings", HttpMethod.Get, parameters);
When i look at the permissionlist in the dbConfig object the token for a permission is null. My thought was that I could instantiate a documentdb client based on the permissionslist but it fails.
public void CreateDocumentDbClient(DbConfig config)
{
client = new DocumentClient(new Uri(config.EndPointUri), config.Permissions);
collectionLink = UriFactory.CreateDocumentCollectionUri(config.DatabaseName, config.CollectionId);
IsInitialized = true;
}
EDITS MADE FROM ANSWER
Just for finish up upon question.
I created a custom class holding both Permission and Token
public class PermissionCustom
{
public Permission Permission { get; set; }
public string Token { get; set; }
}
This makes it possible to create a documentdb client like this:
client = new DocumentClient(new Uri(config.EndPointUri), config.Permissions[0].Token);
So far so good :-) but it doesn't makes it easier to secure your database considering users could have many permissions for different resources. Even though it is properly to make it more secure, the token is readonly in the first place.
According to your code, I have checked this issue and found I could encounter the same issue. When you invoke client.InvokeApiAsync<DbConfig>("Settings", HttpMethod.Get, parameters);, you would send request with the following link:
https://{your-app-name}.azurewebsites.net/api/settings?userid={Settings.AzureUserId}
By using fiddler you could find that the token has been sent to your mobile client as follows:
But when deserialize it to Permission, the token has not been initialized correctly. I found that the token property is read only as follows:
In summary, I recommend that you need to define your custom Permission class and refer to the Permission class provided by DocumentDB client SDK for defining the properties you need within your custom permission class in your mobile client.

How to create service in C# which will perform some operations asynchronously in scenario mentioned below?

I have WebApi Controller as mentioned below. This controller having Update method which will internally call service called CustomerDataService to Update Customer Records.Assume we have n customer records to update.
UpdateMethod in CustomerDataService will perform update and return the update response.
I have requirement to do some heavy processing asynchronously after the update response like manipulating data / managing the data cache. As this processing is time consuming not relevant to the consumer of this API as Update successfully happens So I have to perform this asynchronously. Can I do this with C# with the given scenario? Please suggest.
Note: I do not want to create any batch job to achieve this as I want to perform operation(s) which are user session specific.
Controller
public class CustomerController : ApiController
{
[HttpGet]
public string UpdateCustomer()
{
ICustomerService obj = new CustomerDataService();
return obj.UpdateCustomer(GetCustomerList());
}
private List<CustomerModel> GetCustomerList()
{
return new List<CustomerModel>()
{
new CustomerModel
{
CustomerId="1",
Name="John",
Category="P1"
},
new CustomerModel
{
CustomerId="2",
Name="Mike",
Category="P2"
}
//....n Records
};
}
}
Model
[Serializable]
[DataContract]
public class CustomerModel
{
[DataMember]
public string CustomerId { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Category { get; set; }
}
Interface and CustomerDataService
public interface ICustomerService
{
string UpdateCustomer(List<CustomerModel> customerList);
}
public class CustomerDataService : ICustomerService
{
public string UpdateCustomer(List<CustomerModel> customerList)
{
//Do Data Processing - DB Call
//Return Confirmation Message
return "Data Updated Successfully!!!";
//Needs to perform some processing asynchronously i.e. Call ProcessResults()
}
private void ProcessResults()
{
//DO Processing
}
}
What you are looking for is using async/await in c#, see this article on Microsofts website: Asynchronous Programming with Async and Await. Here is another article with plenty of examples: C# Async, Await.
Once you understand how this works it will be very easy to change your code to take advantage of this pattern. Let us know if you have specific questions or run into problems.

How to add Identity 2.0 users to an object

So I am trying to grasp EF6 and it's use of Identity 2.0 for making a many to many relationship. It is Visual Studio 2013 and the MVC 5 template.
I have a fresh MVC app with the following models:
public class Meeting
{
public Guid MeetingID { get; set; }
public string Title { get; set; }
public virtual ICollection<ApplicationUser> Attendees { get; set; }
}
public class ApplicationUser : IdentityUser
{
public ICollection<Meeting> Meetings { get; set; }
}
Then I scaffold a controller and views for Meetings. Now, for instance, if I just wanted to add every user as an attendee to my meeting, I would imagine that I could modify the Create action to look like the following:
public ActionResult Create(Meeting meeting)
{
if (ModelState.IsValid)
{
meeting.MeetingID = Guid.NewGuid();
db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
db.Meetings.Add(meeting);
db.SaveChanges();
return RedirectToAction("Index");
}
else...
}
However I don't think it's working because I don't see it in my LocalDB and if I add this to the detail view for a meeting I get no results:
#{foreach (var item in Model.Attendees)
{
<li>#item.UserName</li>
}}
As a final note, I have two users in the LocalDB, test and test2.
What tutorial or documentation will allow me to make this work?
* Edit *
So I have tried your suggestion (I'll admit, I am unfamiliar with async and await and how to implement it), and I had to modify the controller to allow me to use await so I'm not sure if I'm doing this correctly now, but I got the following to compile and I get run time error of 'object reference not set to an instance of an object' :
public async Task<ActionResult> Create(Meeting meeting)
{
if (ModelState.IsValid)
{
meeting.MeetingID = Guid.NewGuid();
await db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
db.Meetings.Add(meeting);
db.SaveChanges();
(is it possible I'm missing some setup of my model on Entity Framework? The project is exactly the code shown above plus defaults.)
You're going to kick yourself :)
(Drumroll)
You forgot to add await before your ForEachAsync line:
await db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
Without await the application happily continues on and saves the record, all before that async process has completed.
UPDATE
Most likely you haven't initialized the Attendees collection. Just set it to a new List<ApplicationUser> in your constructor.

Can't seem to expand an Image Navigation property with Breeze JS

I am using MVC, Entity Framework, Durandal and Breeze JS. I've got a user which looks like such (simplified):
public class User : EntityBase<Guid>, IAggregateRoot
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("UserImage")]
public virtual Guid? ImageId { get; set; }
public virtual UserImage UserImage { get; set; }
}
The UserImage class looks like such. I know I should limit the size of the Image. (Maybe this is the issue?):
public class UserImage
{
public Guid Id { get; set; }
[MaxLength]
public byte[] Image { get; set; }
}
I've got an api function on the server to get the current user:
public IQueryable<User> GetCurrentUser()
{
IPrincipal principal = HttpContext.Current.User;
var users = _uow.Users.FindBy(u => u.UserName.Equals(principal.Identity.Name));
if (!users.Any())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized));
}
return users;
}
And two calls on the client which get the current user. The first is in the shell:
function loadCurrentUser() {
return uow.CurrentUser.all().then(function (newUser) {
log('Welcome to the Site ' + newUser[0].FullName() + '!', newUser[0], true);
config.CurrentUser(newUser[0]);
return true;
});
}
The second is in a ManageUser viewmodel:
function activate() {
return uow.CurrentUser.all(['UserImage']).then(function (user) {
self.CurrentUser(user[0]);
return $.when(init()).then(boot());
}).fail(function() {
return router.activate('accounts/login');
});
}
Now I can load an Image into the ManageUser page and save and in fiddler it shows that the ImageId and Image are being sent across to the server. Then I checked the BeforeSaveEntity intercept and shows two entities being saved.
Updated User with ImageId set
New UserImage
The data is also visible in the database. Now when I refresh the Manage User page I can see the two GetCurrentUser calls in fiddler.
From the shell call I can see that the User is being returned and an ImageId is set but no UserImage was sent over because didn't expand the query.
From the Manage User call I see the User is returned but only the ImageId is sent over and the Image object was OMITTED from the JSON.
Has anyone come across this issue with images? All my other expands appear to be working correctly. Does anyone have any examples on using breeze to save just the filepath to the image and possibly using windows azure for media storage?
I know this probably won't answer your question but I would propose not sending the byte array to the client and rather have an Image Handler on the server side that takes an ImageId as a parameter and then return the image with the relevant Content Type set. An example of this can be found here.
By using this approach you could reference your image from HTML using an img tag with the source set to the Image Hander with the relevant ImageId.
An example using knockout for data binding would be:
<a data-bind="attr: {href: '/Image/' + User.ImageId()}"></a>
This approach enables you to easily add caching on both the server and client which will improve performance. It also removed the need to convert the byte array to an image on the client side, which may or may not be a pain.
Edit:
When saving the managed user, post the Image to an Upload action on the ImageHandler (have a look at this article). This action must return the new Id of the image. After you've received the new Id, update the User.ImageId on client side and call SaveChanges on breeze.

Is it possible to inject an instance of object to service at runtime

I have created a plugin which inspects a param in the query string and loads up a user object based on this ID and populates
any request DTO with it. (All my request DTO's inherit from BaseRequest which has a CurrentUser property)
public class CurrentUserPlugin : IPlugin
{
public IAppHost CurrentAppHost { get; set; }
public void Register(IAppHost appHost)
{
CurrentAppHost = appHost;
appHost.RequestFilters.Add(ProcessRequest);
}
public void ProcessRequest(IHttpRequest request, IHttpResponse response, object obj)
{
var requestDto = obj as BaseRequest;
if (requestDto == null) return;
if (request.QueryString["userid"] == null)
{
throw new ArgumentNullException("No userid provided");
}
var dataContext = CurrentAppHost.TryResolve<IDataContext>();
requestDto.CurrentUser = dataContext.FindOne<User>(ObjectId.Parse(requestDto.uid));
if (requestDto.CurrentUser == null)
{
throw new ArgumentNullException(string.Format("User [userid:{0}] not found", requestDto.uid));
}
}
}
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there. Is there a way to make data from plugins globally available to my services? I am also wondering if there is another way of instantiating this object as for my unit tests, the Plugin is not run - as I call my service directly.
So, my question is, instead of using Plugins can I inject a user instance to my services at run time? I am already using IoC to inject different Data base handlers depending on running in test mode or not but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
Below is an example of how I inject my DataContext in appHost.
container.Register(x => new MongoContext(x.Resolve<MongoDatabase>()));
container.RegisterAutoWiredAs<MongoContext, IDataContext>();
Here is an example of my BaseService. Ideally I would like to have a CurrentUser property on my service also.
public class BaseService : Service
{
public BaseService(IDataContext dataContext, User user)
{
DataContext = dataContext;
CurrentUser = user; // How can this be injected at runtime?
}
public IDataContext DataContext { get; private set; }
public User CurrentUser { get; set; }
}
Have you thought about trying to use the IHttpRequest Items Dictionary to store objects. You can access these Items from any filter or service or anywhere you can access IHttpRequest. See the src for IHttpRequest.
Just be mindful of the order that your attributes, services and plugins execute and when you store the item in the Items dictionary.
Adding:
We don't want to use HttpContext inside of the Service because we want use Service in our tests directly.
Advantages for living without it
If you don't need to access the HTTP
Request context there is nothing stopping you from having your same
IService implementation processing requests from a message queue which
we've done for internal projects (which incidentally is the motivation
behind the asynconeway endpoint, to signal requests that are safe for
deferred execution).
http://www.servicestack.net/docs/framework/accessing-ihttprequest
And we don't use http calls to run tests.
So our solution is:
public class UserService
{
private readonly IDataContext _dataContext;
public UserService(IDataContext dataContext)
{
_dataContext = dataContext;
}
public User GetUser()
{
var uid = HttpContext.Current.Request.QueryString["userId"];
return _dataContext.Get<User>(uid);
}
}
and
container.Register(x => new UserService(x.Resolve<IDataContext>()).GetUser()).ReusedWithin(ReuseScope.Request);
This is service signature:
public SomeService(IDataContext dataContext, User user) { }
Any suggestions?
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there
How will your application know about the user if you're not passing the 'userid' in the querystring? Could you store the user data in the Session? Using a Session assumes the client is connected to your app and persists a Session Id (ss-id or ss-pid cookie in ServiceStack) in the client that can be looked up on the Server to get the 'session data'. If you can use the Session you can retrieve the data from your service doing something like
base.Session["UserData"] or base.SessionAs<User>();
Note: you will need to save your User data to the Session
Is there a way to make data from plugins globally available to my services? but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
This sounds like you want a global request filter. You're kind of already doing this but you're wrapping it into a Plugin.

Resources