{"action":"created","issue":{"url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/23","labels_url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/23/labels{/name}","comments_url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/23/comments","events_url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/23/events","html_url":"https://github.com/Glimpse/Glimpse.Prototype/issues/23","id":99263531,"number":23,"title":"Resource module","user":{"login":"nikmd23","id":199026,"avatar_url":"https://avatars.githubusercontent.com/u/199026?v=3","gravatar_id":"","url":"https://api.github.com/users/nikmd23","html_url":"https://github.com/nikmd23","followers_url":"https://api.github.com/users/nikmd23/followers","following_url":"https://api.github.com/users/nikmd23/following{/other_user}","gists_url":"https://api.github.com/users/nikmd23/gists{/gist_id}","starred_url":"https://api.github.com/users/nikmd23/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nikmd23/subscriptions","organizations_url":"https://api.github.com/users/nikmd23/orgs","repos_url":"https://api.github.com/users/nikmd23/repos","events_url":"https://api.github.com/users/nikmd23/events{/privacy}","received_events_url":"https://api.github.com/users/nikmd23/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/labels/Current%20Thinking","name":"Current Thinking","color":"FFFAD5"}],"state":"closed","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/milestones/2","html_url":"https://github.com/Glimpse/Glimpse.Prototype/milestones/2015.08.21%20-%202","labels_url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/milestones/2/labels","id":1242415,"number":2,"title":"2015.08.21 - 2","description":"The theme and goals for this sprint are to finalize as much of the architecture, pluming, framework and extensibility stories as possible.","creator":{"login":"nikmd23","id":199026,"avatar_url":"https://avatars.githubusercontent.com/u/199026?v=3","gravatar_id":"","url":"https://api.github.com/users/nikmd23","html_url":"https://github.com/nikmd23","followers_url":"https://api.github.com/users/nikmd23/followers","following_url":"https://api.github.com/users/nikmd23/following{/other_user}","gists_url":"https://api.github.com/users/nikmd23/gists{/gist_id}","starred_url":"https://api.github.com/users/nikmd23/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nikmd23/subscriptions","organizations_url":"https://api.github.com/users/nikmd23/orgs","repos_url":"https://api.github.com/users/nikmd23/repos","events_url":"https://api.github.com/users/nikmd23/events{/privacy}","received_events_url":"https://api.github.com/users/nikmd23/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":5,"state":"open","created_at":"2015-08-05T16:45:46Z","updated_at":"2015-08-21T23:41:38Z","due_on":"2015-08-21T04:00:00Z","closed_at":null},"comments":1,"created_at":"2015-08-05T18:02:04Z","updated_at":"2015-08-21T23:41:38Z","closed_at":"2015-08-21T23:41:38Z","body":"We need to add back in the concept of resources that can serve assets and data.\r\n\r\nIn the past, Glimpse resources could:\r\n\r\n- Specify their parameters\r\n- Write to the response stream\r\n- Set headers\r\n- Leverage decorators to perform common tasks\r\n\r\nDo we want to keep the same requirements for 2.0?"},"comment":{"url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/comments/133591273","html_url":"https://github.com/Glimpse/Glimpse.Prototype/issues/23#issuecomment-133591273","issue_url":"https://api.github.com/repos/Glimpse/Glimpse.Prototype/issues/23","id":133591273,"user":{"login":"nikmd23","id":199026,"avatar_url":"https://avatars.githubusercontent.com/u/199026?v=3","gravatar_id":"","url":"https://api.github.com/users/nikmd23","html_url":"https://github.com/nikmd23","followers_url":"https://api.github.com/users/nikmd23/followers","following_url":"https://api.github.com/users/nikmd23/following{/other_user}","gists_url":"https://api.github.com/users/nikmd23/gists{/gist_id}","starred_url":"https://api.github.com/users/nikmd23/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nikmd23/subscriptions","organizations_url":"https://api.github.com/users/nikmd23/orgs","repos_url":"https://api.github.com/users/nikmd23/repos","events_url":"https://api.github.com/users/nikmd23/events{/privacy}","received_events_url":"https://api.github.com/users/nikmd23/received_events","type":"User","site_admin":false},"created_at":"2015-08-21T23:41:38Z","updated_at":"2015-08-21T23:41:38Z","body":"### High Level \r\nFrom a high level, resources in Glimpse 2.0 will be pieces of middleware running on branch of the middleware chain. That branch will be split off based on a configurable `baseUri`, which will probably default to `/glimpse`.\r\n\r\nImplemented resources will have a chance to \"chain in\" by implementing `IMiddlewareResourceComposer.Register(IResourceBuilder glimpse)`.\r\n\r\nHere's what that code might look like:\r\n\r\n```C#\r\npublic void Register(IResourceBuilder glimpse)\r\n{   \r\n    glimpse.Resource(\"test\", new Parameters(+Parameter.Hash, Parameter.Version), (ctx, params) => \r\n    {\r\n        var response = ctx.Response;\r\n\r\n        response.Headers.Set(\"Content-Type\", \"text/plain\");\r\n\r\n        var data = Encoding.UTF8.GetBytes(\"Hello world, Glimpse!\");\r\n        await response.Body.WriteAsync(data, 0, data.Length);\r\n    }));\r\n}\r\n```\r\n\r\nThis implementation will automatically generate a metadata entry with the [Uri Template (RFC6570)](https://tools.ietf.org/html/rfc6570) of  `/glimpse/test?hash={hash}{&version}` to be expanded and requested by the client.\r\n\r\n### Detailed Breakdown\r\nThe `IResourceBuilder` type is a special Glimpse type that wraps `IAppBuilder` to ensure proper usage patterns for a resource. (Use vs Map vs Run vs MapWhen)\r\n\r\nMore advanced scenarios will be available through `glimpse.Application`, which will provide access to the underlying `IAppBuilder` instance.\r\n\r\nExtension methods hanging off of `ctx` will be provided to perform common tasks such as `.ConfigureCors()`, `.ConfigureCaching()` or `.AddETag()`.\r\n\r\nThe `params` parameter is a dictionary containing the values associated with the Uri template. In this example, `hash` and `version` are the possible keys. `hash` has been declared to be a required parameter (due to the shorthand `+` unary operator - more explicit syntax is available). `version` is optional. \r\n\r\n### Need To Implement\r\n- [ ] This is mostly implemented, the API just needs some massaging and renames.\r\n- [ ] Helper/extension methods on `ctx`\r\n- [ ] The parameters input list. @nikmd23 has done a spike, code below:\r\n\r\n```C#\r\n/// usage:\r\nvar x = new Parameters(+Parameter.Hash, -Parameter.Version, Parameter.Url, Parameter.Custom(\"foo\", true), Parameter.Custom(\"bar\", false));\r\nConsole.WriteLine(x.GenerateUriTemplate());\r\n\r\n// output:\r\n// ?hash={hash}&foo={foo}{&version,url,bar}\r\n\r\n// implementation:\r\npublic class Parameter\r\n{\r\n    // Example parameters, not the final list\r\n    public static Parameter Hash = new Parameter(\"hash\");\r\n    public static Parameter Url = new Parameter(\"url\");\r\n    public static Parameter Version = new Parameter(\"version\");\r\n    public static Parameter RequestId = new Parameter(\"requestid\");\r\n    public static Parameter Timestamp = new Parameter(\"stamp\");\r\n    public static Parameter Callback = new Parameter(\"callback\");\r\n\r\n    public Parameter(string name) : this(name, false)\r\n    {\r\n    }\r\n\r\n    public Parameter(string name, bool isRequired)\r\n    {\r\n        Name = name;\r\n        IsRequired = isRequired;\r\n    }\r\n\r\n    public static Parameter Custom(string name)\r\n    {\r\n            return new Parameter(name);\r\n    }\r\n\r\n    public static Parameter Custom(string name, bool isRequired)\r\n    {\r\n        return new Parameter(name, isRequired);\r\n    }\r\n\r\n    public string Name { get; private set; }\r\n    public bool IsRequired { get; private set; }\r\n\r\n    public static Parameter operator -(Parameter parameter)\r\n    {\r\n        return new Parameter(parameter.Name, false);\r\n    }\r\n\r\n    public static Parameter operator +(Parameter parameter)\r\n    {\r\n        return new Parameter(parameter.Name, true);\r\n    }\r\n}\r\n\r\npublic class Parameters : List<Parameter>\r\n{\r\n    public Parameters()\r\n    {\r\n    }\r\n\r\n    public Parameters(params Parameter[] parameters)\r\n    {\r\n        foreach (var parameter in parameters)\r\n        {\r\n            base.Add(parameter);\r\n        }\r\n    }\r\n\r\n    public new void Add(Parameter parameter)\r\n    {\r\n        base.Add(parameter);\r\n    }\r\n\r\n    public string GenerateUriTemplate()\r\n    {\r\n        var sb = new StringBuilder();\r\n\r\n        var requiredParams = this.Where(p => p.IsRequired).ToArray();\r\n        if (requiredParams.Length > 0)\r\n        {\r\n            sb.AppendFormat(@\"?{0}={{{0}}}\", requiredParams[0].Name);\r\n            for (int i = 1; i < requiredParams.Length; i++)\r\n            {\r\n                sb.AppendFormat(@\"&{0}={{{0}}}\", requiredParams[i].Name);\r\n            }\r\n        }\r\n\r\n        var optionalParams = this.Where(p => !p.IsRequired).Select(p => p.Name).ToArray();\r\n        if (optionalParams.Length > 0)\r\n        {\r\n            sb.Append(string.Format(\"{{{1}{0}}}\", string.Join(\",\", optionalParams), requiredParams.Length > 0 ? \"&\" : \"?\"));\r\n        }\r\n\r\n        return sb.ToString();\r\n    }\r\n}\r\n```\r\n"}}