I am creating a hot chocolate service as a gateway for merging data from multiple rest endpoints. But I am having a hard time finding any resources that explain how to resolve guids to objects from other endpoints.
For example one service has a db with the products and another service has a db with the reviews.
How do I setup hot chocolate so that this query can be executed?
query {
product(id: $id) {
id
title
review {
comment
}
}
}
```
What you had to do is create an ObjectType to resolve the collection. Something along the lines of...
using HotChocolate.Types;
public class ProductResolvers: ObjectType<Product>
{
protected override void Configure(IObjectTypeDescriptor<Models.Product> descriptor)
{
descriptor.Field(fld => fld.Reviews)
.Resolve(async (context, ct) =>
{
var prodID = context.Parent<Models.Product>().Id);
var ProductReviews= await SomeMethodToGetReviews(prodID);
return ProductReviews;
});
}
}
Once you have the resolver, you will need to register it in your Startup
services.AddGraphQLServer()
.AddQueryType(q => q.Name("Query"))
.AddType<ProductsQuery>()
.AddType<ReviewsQuery>()
.AddType<ProductResolvers>() // <=== Register your product resolver class here
The great thing with Hot Chocolate is that if you do not specify the Reviews in the request, then the Review service will not even be called. Very efficient...
I ended up using a dataloader and a extended type of the product like so:
[ExtendObjectType(typeof(Product))]
public class ProductExtensions
{
public async Task<Review> GetReviewAsync(
[Parent] Product product,
ReviewDataLoader dataLoader)
{
return await dataloader.LoadAsync(product.Id)
}
}
Then added the extended type like so:
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddTypeExtension<ProductExtensions>()
.AddDataLoader<ReviewDataLoader>()
Related
I am using ASP.NET Web API. I want to REST uri to be
GET /api/v1/documents/1234/download or
GET /api/v1/documents/1234?act=download or
GET /api/v1/documents?id=1234&act=download
Is it possible to have multiple ways to call REST API Url? Is it recommended?
I am using Attribute Routes only
[RoutePrefix("api/v1")]
public class DocumentController : ApiController
{
private readonly DomainService _domainService;
public DocumentController(DomainService domainService)
: base(domainService)
{
_domainService = domainService ?? throw new ArgumentNullException(nameof(domainService));
}
[HttpGet]
[Route("documents/{id:int}")]
public async Task<IHttpActionResult> DownloadDocument([FromUri]int id, [FromUri]string act)
{
if (string.IsNullOrEmpty(act) || act.ToUpper() != "DOWNLOAD")
{
return BadRequest("Invalid action parameter.");
}
return await service.DownloadFile(id);
}
}
with above code only GET /api/v1/documents/1234?act=download works. Is it possible to configure route in a such way that all 3 routes will invoke same action method?
You can add as many Route attributes as required to each method.
So you could do this to your method:
[Route("documents")] // matches /documents?id=123&act=download
[Route("documents/{id:int}")] // matches /documents/123?act=download
[Route("documents/{id:int}/{act}")] // matches /documents/123/download
Personally I think this is quite long-winded, and would try to stick to a single style (the last one if I could choose), but I guess it could depend on your requirements.
I'm trying to understand how to work with TableController in Azure Mobile Apps. Here's the sample TodoItemController:
public class TodoItemController : TableController<TodoItem>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<TodoItem>(context, Request, Services);
}
// GET tables/TodoItem
public IQueryable<TodoItem> GetAllTodoItems()
{
return Query();
}
// GET tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<TodoItem> GetTodoItem(string id)
{
return Lookup(id);
}
// PATCH tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<TodoItem> PatchTodoItem(string id, Delta<TodoItem> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/TodoItem
public async Task<IHttpActionResult> PostTodoItem(TodoItem item)
{
TodoItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/TodoItem/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteTodoItem(string id)
{
return DeleteAsync(id);
}
}
Ideally, I'd like to avoid passing around whole models like TodoItem to reduce incoming/outgoing bandwidth and limit clients to only what they should care about. If I were to do that, how would offline sync and client-side SDKs be affected?
Is TableController intended for simple CRUD operations as suggested above? Any examples out on the Internet with complex queries?
The Mobile Apps TableController is the basis for an OData based CRUD interface. You will always transmit an entire model (which is based on an EntityData model, so it has four additional fields - version, createdAt, updatedAt and deleted) to the client. However, the client can use an OData search to get a specific set of entities. For more information on OData, check out http://www.odata.org/
In the specific case of Offline Sync and using the Mobile Apps SDK for clients, the client SDK will issue a GET but limit the results to the last update time (which will be zero for the first request and hence will get everything). It will then push up the changes from the client. In certain cases (where the version does not match), it will have to do conflict resolution. Check out "How Offline Sync Works" in their documentation: https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-offline-data-sync-preview/
I can't really understand when to use flatmap instead of map, nor do I find a good example.
Can you come up with a good scenrio to choose flat map over map?
Thanks.
For example, we want to do 2 requests, A and B, over web. However, request B must be requested after request A finishes because request B needs some result of request A. This is a good scenrio of flatMap. The example codes are as follow:
interface Movie {
}
interface UserInfo {
List<Long> getFavoriteMovies();
}
public Observable<UserInfo> requestUserInfo(long userId) {
// ...
}
public Observable<List<Movie>> requestMovieList(List<Long> movieIds) {
// ...
}
public Observable<List<Movie>> requestUserMovieList(long userId) {
return requestUserInfo(userId).flatMap(new Func1<UserInfo, Observable<List<Movie>>>() {
#Override
public Observable<List<Movie>> call(UserInfo user) {
return requestMovieList(user.getFavoriteMovies());
}
});
}
In addition, Ben Christensen's slide has some good examples of RxJava: https://speakerdeck.com/benjchristensen/rxjava-goto-aarhus-2013
I have written a REST service using Web API and after reading sections of this Web API Design from Brian Mulloy, was trying to figure out how I could implement associations with Web API.
Web API Design Extract:
Associations
Resources almost always have relationships to other
resources. What's a simple way to express these relationships in
aWebAPI?
Let's look again at the API we modeled in nouns are good,
verbs are bad -theAPI that interacts with our dogs resource.
Remember, we had two base URLs: /dogs and dogs/1234.
We're using HTTP
verbs to operate on the resources and collections. Our dogs belong to
owners. To get all the dogs belonging to a specific owner, or to
create a new dog for that owner, do a GET or a POST:
GET /owners/5678/dogs
POST /owners/5678/dogs
Now, the relationships can be
complex. Owners have relationships with veterinarians, who have
relationships with dogs, who have relationships with food, and so on.
It's not uncommon to see people string these together making a URL 5
or 6 levels deep. Remember that once you have the primary key for one
level, you usually don't need to include the levels above because
you've already got your specific object. In other words, you shouldn't
need too many cases where a URL is deeper than what we have above
/resource/identifier/resource.
So I tried to add a controller method for the association like follows:
public class EventsController : ApiController
{
// GET api/events
public IEnumerable<Event> Get()
{
// get list code
}
// GET api/events/5
public Event Get(int id)
{
// get code
}
// POST api/events
public void Post([FromBody]Event evnt)
{
// add code
}
// POST api/events/5
public void Post(int id, [FromBody]Event evnt)
{
// update code
}
// DELETE api/events/5
public void Delete(int id)
{
// delete code
}
// GET api/events/5/guests
public IEnumerable<Guest> Guests(int id)
{
// association code
}
}
I also modified my route templates to the following:
config.Routes.MapHttpRoute("ApiWithAssociations",
"api/{controller}/{id}/{action}");
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
Unfortunately, when I do an update/post of the event resource I now get a HTTP 500 Internal Server Error with a response body stating
Multiple actions were found that match the request
I've tried modifying the route templates in conjunction with adding System.Web.Http.HttpPostAttribute (and other HTTP verbs) as well but to no avail.
Has anyone tried this and got it working? Any help would be appreciated. If it is absolutely not possible to have multiples for an http verb then I guess I'll have to abandon associations with my REST service.
EDIT: SOLUTION
Using Radim Köhler's answer, I was able to get this working. Add the HttpGetAttribute to the Guests method like so:
// GET api/event/5/guests
[HttpGet]
public IEnumerable<Guest> Guests(int id)
{
// association code
}
And added an addition route to cater for the default GET action like follows:
config.Routes.MapHttpRoute("DefaultGet",
"api/{controller}/{id}",
new {action = "Get"},
new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)});
config.Routes.MapHttpRoute("ApiWithAssociations",
"api/{controller}/{id}/{action}");
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new {id = RouteParameter.Optional});
The solution, could be in an explicit POST mapping
Just add new definition, which will be used for events/5 POST
// explicit Post() mapping
config.Routes.MapHttpRoute(
name: "DefaultPost",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "Post" }
, constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
);
// existing
config.Routes.MapHttpRoute("ApiWithAssociations",
"api/{controller}/{id}/{action}");
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
I'm building a RESTful interface on a Grails 2.1.1 application. How should I implement search operations? I don't want to repeat huge amounts of code, which my current thinking would require.
The server structure is quite normal Grails-MVC: domain classes represent data, controllers offer the interface and services have the business logic. I use command objects for data binding in controllers but not on service methods. The client is a web UI. My goal is to have search URLs like this:
/cars/?q=generic+query+from+all+fields
/cars/?color=red&year=2011
(I'm aware of the debate on the RESTfulness of this kind of URLs with query strings: RESTful URL design for search. While I think this is the best model for my purpose, I'm open to alternatives if they make the API and the implementation better.)
As you can see from the code examples below my problem is with the second kind of URL, the field-specific search. In order to implement this kind of search operation for several domain classes with lots of fields my method signatures would explode.
There probably is a "Groovy way" to do this but I'm still a bit of a n00b in finer Groovy tricks :)
Domain:
class Car {
String color
int year
}
Controller:
class CarsController {
def carService
def list(ListCommand cmd) {
def result
if (cmd.q) {
result = carService.search(cmd.q, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
else {
result = carService.search(cmd.color, cmd.year, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
render result as JSON
}
class ListCommand {
Integer max
Integer offset
String order
String sort
String q
String color // I don't want this field in command
int year // I don't want this field in command
static constraints = {
// all nullable
}
}
// show(), save(), update(), delete() and their commands clipped
}
Service:
class CarService {
List<Car> search(q, max=10, offset=0, order="asc", sort="id") {
// ...
}
List<Car> search(color, year, max=10, offset=0, order="asc", sort="id") {
// ...
}
}
UrlMappings:
class UrlMappings {
static mappings = {
name restEntityList: "/$controller"(parseRequest: true) {
action = [GET: "list", POST: "save"]
}
name restEntity: "/$controller/$id"(parseRequest: true) {
action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
}
}
}
You can get all this parameters from params, like:
result = carService.search(params.color, params.year as Integer, cmd.max, cmd.offset, cmd.order, cmd.sort)
All values of params map are strings, so you should convert it to appropriate data structures in controller (and it's better to check that params.year is actual number)
Update
If you don't want to writer field names, you can pass it as a Map:
resutl = carService.search(params)
where
List<Car> search(Map params)