Git Product home page Git Product logo

halcyon's People

Contributors

dixonds avatar isaacsanch-progenity avatar jmpease avatar johncmckim avatar mkeymolen avatar mladenb avatar samuelabj avatar wagich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

halcyon's Issues

Accept Header Not Working

I tried following the instructions in the README to get Halcyon to output plain json if the accept header is simply application/json instead of application/hal+json, but it doesn't seem to be working. No matter how I fiddle with it, it always returns the HAL formatted results (with a content-type header of application/json in the response).

I tried cloning the Halcyon project and running the test project Halcyon.Tests.SelfHost.Mvc, which appeared to be setup the way the README described, but I got the same results as with my own project. I feel like I'm missing something important about getting Halcyon to handle the Accept header correctly. Any help would be greatly appreciated.

Thanks.

Support for embedded objects

I would like to embed a resource object rather than a collection of resource objects.

Is there a specific reason why only AddEmbeddedCollection is currently supported?

OData not working with HALResponse

[Route("{id}/myData")]
    [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
    public IActionResult Get(string id)
    {
        var myData = _Service.GetMyData(id); // return IQueryable
         var acceptHeader = Context.Request.Headers[AcceptHeaderKey];

        if (acceptHeader == AcceptHeaderValue) // checking Accept Header for application/hal+json
        {                
            var data = myData.ToList();

            var response = new HALResponse(new { data.Count }).AddSelfLink(this.Context.Request).AddEmbeddedCollection("values",
                data, new[]
                {
                    new Link("self", this.Context.Request.Path + "{id}")
                });
            return Ok(response);
        }
       
	   return Ok(myData);
    }
  1. For Normal IQueryable data (myData ), OData is working fine.
  2. But with HALResponse, Odata is not working fine.

Returned error is:

The query specified in the URI is not valid. The requested resource is not a collection. Query options $filter, $orderby, $count, $skip, and $top can be applied only on collections.

Query Sent:

http://localhost:49600/api//16777217/myData?$top=2

Odata needs Collection type to work and since HALResponse is not a collection, it throws an error.

Is there any way to get bypass this error and make HAL work with OData?

How can I accurately represent the dto that will be returned to a REST call with Swagger?

Hello,

We're trying to do these two things in an Asp.Net core Api:

  • leverage the halcyon package to add _links to our Dto models as our flavor of HAL.
  • leverage NSwag to generate swagger documentation of what the api looks like, including what the models look like.

We have Dto models similar to this:

public class FooDto
{
  public int Id {get; set;}
  public string Value {get; set}
}

Which when returned out of the API have a self link added by creating a new Halcyon.HAL.HALResponse , which looks like this (sorry for adding comments to json but i wanted to be clear):

// run-time json
{
  "Id": 1,
  "Value": "some value",
  "_links": {
    "self": {
      "href": "/foo/1"
    }
  }
}

The Problem
But when we instruct NSwag about the Dto with an attribute on the controller like this [SwaggerResponse(HttpStatusCode.OK, typeof(FooDto))]
The swagger page does not include the "_links" field in the representation of the return model.

// swagger documentation json
{
  "Id": "int",
  "Value": "string"
  // Links section should be here
}

If we give Swagger the HALResponse type: [SwaggerResponse(HttpStatusCode.OK, typeof(HALResponse))], the swagger page simply displays a json model with "model" and "config" sections and no other content.

I've search around the web without finding a solution that still uses the Halcyon.HAL.HALResponse. I did see that issue #56 mentioned Swagger but it didn't seem to touch on this issue except in one un-responded-to comment on the bottom.

I can see a way to get swagger the correct model- add the _links property to the Dtos, but that means throwing out Halcyon, so I wanted to check here first.

The Issue:
How can we register a type such that NSwag/Swagger will properly document the model that will be returned from the API when returning something like this:

            return this.HAL(fooModel, new Link[] {
                new Link("self", "/api/foo/{id}")
            });

JsonHalOutputFormatter does not implement IApiResponseTypeMetadataProvider

We're using Swashbuckle [1] for Swagger tooling in our ASP.NET Core-based API project and ran into an issue where the only listed parameter content type available in the Swagger UI [e.g. 2] was "text/plain" when using the JsonHalOutputFormatter, after removing the JsonOutputFormatter (as I did in #48 to fix #47). After further investigation, it appears this is because the JsonOutputFormatter implements IApiResponseTypeMetadataProvider [3], while the JsonHalOutputFormatter does not. According to the docs [3]:

An IOutputFormatter should implement this interface to expose metadata information to an IApiDescriptionProvider.

So assuming that Swashbuckle depends on an implementation of IApiDescriptionProvider, I made a change to the JsonHalOutputFormatter to impl IApiResponseTypeMetadataProvider and everything worked as expected....including listing of configured HAL media types.

Will submit this change as a PR for consideration.

1 - https://github.com/domaindrivendev/Swashbuckle.AspNetCore
https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.mvc.apiexplorer.iapiresponsetypemetadataprovider
2 - http://petstore.swagger.io/
3 - https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.mvc.apiexplorer.iapiresponsetypemetadataprovider

Support for ASPNetCore 1.0.1

i'm trying to get my web api project in aspnetcore (.netCoreApp, version 1.0) working with halcyon, but importing Halcyon.Mvc v2.1.0 gives following error:

Package Halcyon.Mvc 2.1.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Halcyon.Mvc 2.1.0 supports:
- net451 (.NETFramework,Version=v4.5.1)
- net46 (.NETFramework,Version=v4.6)
One or more packages are incompatible with .NETCoreApp,Version=v1.0.

halcyon is not working on the .Net 6

halcyon is not working in the .net 6 and throwing following issue:

System.TypeLoadException: 'Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.JsonSerializerSettingsProvider' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

on the below code:

c.OutputFormatters.Add(new JsonHalOutputFormatter(
    halJsonMediaTypes: new string[] {
       ApplicationMediaType.HALPlusJson,
       ApplicationMediaType.HALPlusJsonVendor,
       ApplicationMediaType.HALPlusJsonVendorV1
    }));

Strong Name

Please strong name Halcyon. It's unusable from signed code:-(

Does Halcyon support link arrays

Does Halcyon support links as arrays such as:

"_links": { "self": { "href": "/orders" }, "curies": [{ "name": "ea", "href": "http://example.com/docs/rels/{rel}", "templated": true }], "next": { "href": "/orders?page=2" }, "ea:find": { "href": "/orders{?id}", "templated": true }, "ea:admin": [{ "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" }] },}

Collection example

Could we get an example returning a resource collection?
I want to go to /api/foo and get back a collection of foo resources, each with a self link.
Something like this:

[
    {    
         "_links": {
            "self": { "href": "/api/foo/1" }
         },
        "id": 1,
        "type": "foo"
    },
    {    
         "_links": {
            "self": { "href": "/api/foo/2" }
         },
        "id": 2,
        "type": "foo"
    }
]

I can't see any way to do that with HAL though?

Support Arrays of Links

Items in the links Object can return a single link or an Array of Links

i.e.

"_links": {
    "self": { "href": "/orders" },
    "curies": [{ "name": "ea", "href": "http://example.com/docs/rels/{rel}", "templated": true }],
    "next": { "href": "/orders?page=2" },
    "ea:find": {
        "href": "/orders{?id}",
        "templated": true
    },
    "ea:admin": [{
        "href": "/admins/2",
        "title": "Fred"
    }, {
        "href": "/admins/5",
        "title": "Kate"
    }]
}

Support for Arrays of links needs to be added.

Add templating support to link titles

It would be nice to have templating support for link titles so that it's possible to have unique titles without having to manually loop over the embedded items.

.AddEmbeddedCollection("children", category.Children, new List
{
new Link("self", "categories/{Id}", "{Title}", "GET"),
})

Would you be willing to let me create a pull-request for this?

How to return URI templates in links?

The HAL specification seems to allow for URI templates in links, with parameters denoted in braces, e.g. "href": "/orders{?id}". How is this achieved in Halcyon? Using this syntax in a link URL causes Halcyon to attempt to substitute the parameter with a value from the model, and using double braces to escape the template causes an ArgumentException to be thrown in TemplateExtensions.SubstituteParams().

I can't turn off parameter substitution for this link, as I have one parameter that can be substituted and one that must be passed back as a parameter name to the client for substitution.

Asp.Net Core Support

Support ASP.NET Core and MVC 6. This will involve splitting the project into multiple projects so each can be supported independently.

Links should always be URL Encoded

Currently there is not enforcement of URL encoded Links returned by Halcyon.

All links should be url encoded so that clients always can expect valid links.

Suggestion is that Halcyon assumes responsibility and always url encodes all links

Convert Halcyon projects to .NET Core

Convert the main Halcyon project to .NET Core to better support the Halcyon.MVC project. It still need to support projects targeting .NET 4.5 and above.

Release Nuget Packages with Git Tags

The current CI / CD process is a little crazy. Nuget packages are getting released too frequently (on every master build). This needs to be changed to release on tags only.

  • Start using tags in the repo when deploying packages
  • Change AppVeyor to deploy on tags
  • Deploy pre-release and stable based on tags and branches?

Exception adding OutputFormatter in .netcore 3.0

I am trying to use this package with asp.net core 3.0 and running into some issues.

Adding the OutputFormatter in ConfigureServices throws an exception, I am guessing changes in .netcore 3.0 json implementations must be the reason.

`c.OutputFormatters.Add(new JsonHalOutputFormatter(new[] { "application/hal+json", "application/vnd.example.hal+json", "application/vnd.example.hal.v1+json" }));

System.TypeLoadException: 'Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.JsonSerializerSettingsProvider' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

Is there any fix for this?
Will there be a .netcore 3.0 upgrade for this package?

Thanks.

Getting the mebedded recousres with the correct Links

Maybe a stupid question, but I've been strugling with this for 3 days now... and have tried many different approaches

My Expected rsult would be to do a GetAll for Customers which has Projects as an embedded collection.

This is my Code.

        public override IActionResult Get()
        {
            var customers = _repository.GetAll();
            var result = new {
                _items = new[] {
                    customers.Select(c =>
                     new HALResponse(new {c.Id, c.Address, c.Name, c.CreatedAt, c.UpdatedAt})
                    .AddLinks(new Link(Link.RelForSelf, $"/api/customers/{c.Id}"))
                )
            }};

            var projects2 = customers.Select(c => new {
                _items = c.Projects
            });

            var projects = new {
                _items = new[] {
                    customers.Select(c =>
                     new HALResponse(new {c.Projects})
                     .AddLinks(new Link(Link.RelForSelf, "/api/projects/{Id}"))
                )
            }};

            return new HALResponse(result).AddLinks(new Link(Link.RelForSelf, "/api/customers"))
                .AddEmbeddedResource("prjoects", projects)
                .ToActionResult(this, System.Net.HttpStatusCode.OK);
        }

The Customer part is correct, yet the Prjects are not working correctly
It has a problem with the Levels, it is nested to much and missing the self Links

    "_embedded": {
        "prjoects": {
            "_items": [
                [
                    {
                        "projects": [
                            {
                                "id": 1,
                                "name": "1",
                                "customerId": 1,
                                "createdAt": "2018-02-15T10:27:15.1873444",
                                "updatedAt": "2018-02-15T10:27:15.1874564",
                                "customer": null
                            },
                            {
                                "id": 2,
                                "name": "2",
                                "customerId": 1,
                                "createdAt": "2018-02-15T10:27:15.1977186",
                                "updatedAt": "2018-02-15T10:27:15.1977211",
                                "customer": null
                            }
                        ],
                        "_links": {
                            "self": {
                                "href": "/api/projects/"
                            }
                        }
                    },
            ....etc

So my problems are

  • this nesting:
    "_embedded": {
        "prjoects": {
            "_items": [
                [
                    {
                        "projects": [
                            {
                                "id": 1,
  • And fixing the Links
                        "_links": {
                            "self": {
                                "href": "/api/projects/"
                            }

Add Attribute support for models

Add the ability to create a HAL Response from a model using attributes. Something similar to the following:

[HalModel("~/api/")] // Link base & force hal settings
[HalLink("self", "orders/{value}")] // add links to class
[HalLink("users", "users/{value}")]
public class OrderDto {
    public Guid Id { get; set; }
    public Guid UserId { get; set; }

    public DateTime MadeDate { get; set; }
    public DateTime FulfilledDate { get; set; }

    // mark certain items as embedded - possibly support links here
    [HalEmbedded("items"), HalLink("orders/{orderid}/items/{id}")]
    public IEnumerable<OrderItemDto> Items { get; set; }
}

The user should then be able to return the model using

[HttpGet, Route("orders/{Id:guid}")]
public IHttpActionResult GetOrder(Guid Id) {
   return this.Ok(new OrderDto());
}

This would then be automatically serialised into HAL based on the accept type.

Allow passing of default HALModelConfig to HALAttributeConverter

Rather than having to explicitly set configuration values in each HalModel attribute when we have settings that should be used by every model, it'd be nice to be able to pass that default HALModelConfig to the HALAttributeConverter instance. If the attributes don't have config values set, the default config will be used, otherwise the attribute values will override the default.

So basically where I'd currently have to use something like

[HalModel("foo", true)]

on every model...I'd like to be able to do something like

// Replace default json output formatter w/ the one supporting HAL
options.OutputFormatters.RemoveType<JsonOutputFormatter>();
var serializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings();
serializerSettings.ContractResolver = new JsonHalAttributesContractResolver();
options.OutputFormatters.Add(new JsonHalOutputFormatter(serializerSettings,
    converters: new HALAttributeConverter(new HALModelConfig {
        LinkBase = "foo",
        ForceHAL = true
    })));

Then I can just use

[HalModel]

until I need to override.

Does this make sense? I'll submit a PR for consideration.

Automatically generate links

Manually creating links can be a little cumbersome. It would be great if links could be automatically generated from routes or a specialised attribute.

Unexpected output when using HALAttributeConverter

I've noticed when using the HALAttributeConverter, the serialized output contains the embedded properties on the model in addition to where I'd expect them, under "_embedded":

{
  "id": 0,
  "firstName": "Bob",
  "lastName": "Smith",
  "display_name": "Bob Smith",
  "pets": [],
  "favouritePet": {
    "id": 0,
    "name": "Benji",
    "petToys": [
      {
        "name": "Rubber Bone"
      }
    ]
  },
  "_links": {
    "self": {
      "href": "~/api/person/0"
    },
    "person": {
      "href": "~/api/person/0"
    }
  },
  "_embedded": {
    "pets": [],
    "favouritePet": {
      "id": 0,
      "name": "Benji",
      "petToys": [
        {
          "name": "Rubber Bone"
        }
      ],
      "_links": {
        "self": {
          "href": "~/api/pets/0"
        }
      },
      "_embedded": {
        "toys": [
          {
            "name": "Rubber Bone",
            "_links": {
              "self": {
                "href": "~/api/pets//toys/Rubber%20Bone"
              }
            }
          }
        ]
      }
    }
  }
}

I think the desired behavior would be to ignore the model properties w/ the HalEmbedded attribute, with a result like:

{
  "id": 0,
  "firstName": "Bob",
  "lastName": "Smith",
  "display_name": "Bob Smith",
  "_links": {
    "self": {
      "href": "~/api/person/0"
    },
    "person": {
      "href": "~/api/person/0"
    }
  },
  "_embedded": {
    "pets": [],
    "favouritePet": {
      "id": 0,
      "name": "Benji",
      "_links": {
        "self": {
          "href": "~/api/pets/0"
        }
      },
      "_embedded": {
        "toys": [
          {
            "name": "Rubber Bone",
            "_links": {
              "self": {
                "href": "~/api/pets//toys/Rubber%20Bone"
              }
            }
          }
        ]
      }
    }
  }
}

I'm also noticing when converted back using ToPlainResponse, as may be the case w/ "application/json" content type, the embedded properties get merged back into the model resulting in duplication:

{
  "id": 0,
  "firstName": "Bob",
  "lastName": "Smith",
  "display_name": "Bob Smith",
  "pets": [],
  "favouritePet": {
    "id": 0,
    "name": "Benji",
    "petToys": [
      {
        "name": "Rubber Bone"
      },
      {
        "name": "Rubber Bone"
      }
    ],
    "toys": [
      {
        "name": "Rubber Bone"
      }
    ]
  }
}

I did some investigation and it looks like it will be a bit of work/overhead to remove these from the model during "conversion" in HALAttributeConverter. The current workaround I'm using for this is to use a custom contract resolver that simply ignores properties with the the HalEmbedded attribute:

    public class JsonHalAttributesContractResolver : CamelCasePropertyNamesContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var properties = base.CreateProperties(type, memberSerialization);

            // Ignore properties w/ HalEmbedded attribute
            return
                properties.Where(p => !p.AttributeProvider.GetAttributes(typeof(HalEmbeddedAttribute), true).Any())
                    .ToList();
        }
    }

wired in Startup like

            services.AddMvc(options =>
            {
                // Replace default json output formatter w/ the one supporting HAL
                options.OutputFormatters.RemoveType<JsonOutputFormatter>();
                var serializerSettings = JsonSerializerSettingsProvider.CreateSerializerSettings();
                serializerSettings.ContractResolver = new JsonHalAttributesContractResolver();
                options.OutputFormatters.Add(new JsonHalOutputFormatter(serializerSettings,
                    converters: new HALAttributeConverter()));
            });

While this was pretty simple and appears to get around both issues above, it feels a little awkward...basically in order for the HALAttributeConverter to work as expected you'd have to remember to wire in a similar contract resolver.

Any thoughts on this?

Output is not using camel case

The default JsonOutputFormatter uses camel case formatting. See aspnet/Mvc#4283

It appears the JsonHalOutputFormatter is not using the same output settings, however, so when requesting a type handled by this formatter the output looks like this:

{
  "id": 1,
  "type": "foo",
  "CamelCase": "this should be camelCase",
  "_links": {
    "foo:bar": {
      "href": "/api/foo/1/bar"
    },
    "self": {
      "href": "/api/foo/1",
      "method": "GET"
    }
  }
}

whereas a request for "application/json" handled by the JsonOutputFormatter results in this:

{
  "id": 1,
  "type": "foo",
  "camelCase": "this should be camelCase",
  "_links": {
    "foo:bar": {
      "href": "/api/foo/1/bar"
    },
    "self": {
      "href": "/api/foo/1",
      "method": "GET"
    }
  }
}

Framework DNXCore v5.0 Not Supported

I created a brand-new ASP.NET Core project, and immediately installed Halcyon.Mvc. It gave this error:

The dependency Halcyon.Mvc 1.1.0-alpha in project ConnectApis does not support framework DNXCore,Version=v5.0

I decided to create a brand new .NET Core project, and drop your source code into it. From that I created a NuGet package and have installed it into my original ASP.NET Core project. It seems to work fine. The project.json I have in this arrangement looks like this:

"frameworks": {
  "net451": { },
  "dotnet5.4": {
    "dependencies": {
      "Microsoft.CSharp": "4.0.1-beta-23516",
      "System.Collections": "4.0.11-beta-23516",
      "System.Linq": "4.0.1-beta-23516",
      "System.Runtime": "4.0.21-beta-23516",
      "System.Threading": "4.0.11-beta-23516"
    }
  }
},
"dependencies": {
  "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
  "Newtonsoft.Json": "8.0.3",
  "Tavis.UriTemplates": "1.0.0"
}

Unable to return non-HAL results after disabling default JsonOutputFormatter

This is sort of related to #47. Removing the default JsonOutputFormatter does allow accept headers to work correctly. However, doing so makes returning anything that is not a HalResponse (i.e. simply trying to return Ok(ObjectModel)) return a 406 Not Acceptable when the link is accessed.

This doesn't seem totally unreasonable. There's no longer a formatter for application/json enabled in the application, and so it seems it's expected that every controller will only return HalResponse objects in order to work around this. The problem I have with this is migrating an existing API to start using HAL. We were hoping to make a gradual change to our API, introducing new, compliant endpoints while phasing out older, non-compliant endpoints, but it seems it won't really be possible.

If it's not a design goal of the halcyon team to support such mixed environments, I understand. However, I theorize that it should be possible to register the JsonHalOutputFormatter as the handler for application/json and produce non-HAL output. I may attempt a pull request doing this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.