From 12de4552efaa39b5fea0b6363594dfeaffd8f297 Mon Sep 17 00:00:00 2001 From: Jovan Popovic Date: Tue, 28 Mar 2017 12:44:02 +0200 Subject: [PATCH] Added OData in IVS demo Added nometadata and minimal metadata OData services in IVS demo. Minor update in bcp (Belgrade Demo) --- .../sql-scripts/bcp.sql.tt | 19 +- .../Controllers/ODataController.cs | 238 ++++++++++++++++++ .../Controllers/PeopleController.cs | 3 +- .../linqpad/PeopleOData.linq | 13 + .../demos/ivs-people-register/project.json | 2 +- .../sql-scripts/1 setup.sql | 9 +- 6 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 samples/demos/ivs-people-register/Controllers/ODataController.cs create mode 100644 samples/demos/ivs-people-register/linqpad/PeopleOData.linq diff --git a/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt b/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt index c7526b6b..f1c03045 100644 --- a/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt +++ b/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt @@ -1,8 +1,23 @@ <#@ output extension=".sql" #> <#@ template language="C#" hostspecific="True" #> - SELECT * FROM OPENROWSET(BULK '<#=this.Host.ResolvePath("..\\logs") #>\log-20170203.ndjson', - FORMATFILE = '<#=this.Host.ResolvePath("..\\logs") #>\linedelimited.fmt' ); + FORMATFILE = '<#=this.Host.ResolvePath("..\\logs") #>\linedelimited.fmt' ) as logs; + + + DROP TABLE IF EXISTS Logs + +--{"Timestamp":"2017-02-03T08:33:32.0776155+01:00","Level":"Information","MessageTemplate":"{HostingRequestFinished:l}","Properties":{"ElapsedMilliseconds":154.2332,"StatusCode":200,"ContentType":null,"HostingRequestFinished":"Request finished in 154.2332ms 200 ","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Internal.WebHost","RequestId":"0HL2C0RQ64LNN","RequestPath":"/"},"Renderings":{"HostingRequestFinished":[{"Format":"l","Rendering":"Request finished in 154.2332ms 200 "}]}} + CREATE TABLE Logs ( + Data NVARCHAR(MAX), + Timestamp AS CAST(JSON_VALUE(Data, '$.Timestamp') as datetime2), + Level AS CAST(JSON_VALUE(Data, '$.Level') as nvarchar(40)), + ElapsedMilliseconds AS CAST(JSON_VALUE(Data, '$.Properties.ElapsedMilliseconds') AS float), + StatusCode AS CAST(JSON_VALUE(Data, '$.Properties.StatusCode') AS int) +) + +BULK INSERT Logs +FROM '<#=this.Host.ResolvePath("..\\logs") #>\log-20170203.ndjson' +WITH (FORMATFILE = '<#=this.Host.ResolvePath("..\\logs") #>\linedelimited.fmt') diff --git a/samples/demos/ivs-people-register/Controllers/ODataController.cs b/samples/demos/ivs-people-register/Controllers/ODataController.cs new file mode 100644 index 00000000..5ec90023 --- /dev/null +++ b/samples/demos/ivs-people-register/Controllers/ODataController.cs @@ -0,0 +1,238 @@ +using Belgrade.SqlClient; +using Microsoft.AspNetCore.Mvc; +using SqlServerRestApi; +using System; +using System.Text; +using System.Threading.Tasks; + +// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 +namespace Register.Controllers +{ + [Route("api/[controller]")] + public class ODataController : Controller + { + IQueryPipe pipe = null; + TableSpec tableSpec = new TableSpec("dbo", "People") + .AddColumn("id", "int", isKeyColumn: true) + .AddColumn("name","nvarchar") + .AddColumn("surname","nvarchar") + .AddColumn("address", "nvarchar") + .AddColumn("town", "nvarchar"); + + + public string ODataMetadataUrl + { + get + { + return this.Request.Scheme + "://" + this.Request.Host + "/api/odata"; + } + } + + public ODataController(IQueryPipe sqlQueryService) + { + this.pipe = sqlQueryService; + } + + [Produces("application/json; odata.metadata=minimal")] + [HttpGet] + public string Get() + { + try + { + return ODataHandler.GetRootMetadataJsonV4(ODataMetadataUrl, new TableSpec[] { tableSpec }); + } catch (Exception ex) + { + return ex.Message; + } + } + + + [Produces("application/xml")] + [HttpGet("$metadata")] + public string Metadata() + { + try { + return ODataHandler.GetMetadataXmlV4(new TableSpec[] { tableSpec }, "Demo.Models"); + } catch (Exception ex) + { + return ex.Message; + } + } + + // GET api/odata/People + [HttpGet("People")] + public async Task People() + { + await this + .ODataHandler(tableSpec, this.pipe, ODataHandler.Metadata.MINIMAL) + .OnError(ex => Response.Body.Write(Encoding.UTF8.GetBytes(ex.Message), 0, (ex.Message).Length)) + .Get(); + } + + // GET api/odata/People/$count + [HttpGet("People/$count")] + public Task PeopleCount() + { + return this.People(); + } + + /// + /// Endpoint that exposes People information using OData protocol. + /// + /// OData response. + // GET api/OData/People + [HttpGet("Product")] + public async Task OData() + { + Response.ContentType = "application/json;odata.metadata=minimal;odata=minimalmetadata"; + //var body = Encoding.UTF8.GetBytes(@"{""@odata.context"":""http://localhost:59934/api/odata/$metadata#Product"",""value"":[{""id"":1,""name"":""Test""}]}"); + + //await Response.Body.WriteAsync(body, 0, body.Length); + + await this + .ODataHandler(tableSpec, pipe) + .Get(); + } + + + + /// + /// Method that process server-side processing JQuery DataTables HTTP request + /// and returns data that should be shown. + /// + /// + // GET api/People + //[HttpGet] + public string GetOld() + { + //Response.ContentType = "application/json;odata=minimalmetadata;streaming=true;charset=utf-8"; + + //return "{\"odata.metadata\":\"http://services.odata.org/V3/Northwind/Northwind.svc/$metadata\",\"value\":[{\"name\":\"People\",\"url\":\"People\"}]}"; + //Response.ContentType = "application/json;odata=nometadata;streaming=true;charset=utf-8"; + //return "{\"odata.metadata\":\"http://services.odata.org/V3/Northwind/Northwind.svc/$metadata\",\"value\":[{\"name\":\"People\",\"url\":\"People\"}]}"; + + // radi: + //Response.ContentType = "application/xml;charset=utf-8"; + //return @"DefaultCategoriesCustomerDemographicsCustomersEmployeesOrder_DetailsOrdersProductsRegionsShippersSuppliersTerritoriesAlphabetical_list_of_productsCategory_Sales_for_1997Current_Product_ListsCustomer_and_Suppliers_by_CitiesInvoicesOrder_Details_ExtendedsOrder_SubtotalsOrders_QriesProduct_Sales_for_1997Products_Above_Average_PricesProducts_by_CategoriesSales_by_CategoriesSales_Totals_by_AmountsSummary_of_Sales_by_QuartersSummary_of_Sales_by_Years"; + + Response.ContentType = "application/xml;charset=utf-8"; + return @"DefaultProduct"; + } + + //[HttpGet("$metadata")] + //public string Metadata1() + //{ + // Response.ContentType = "application/xml;charset=utf-8"; + + // return ODataMetaData(this.tableSpec, "Models", "Product"); + + //} + + + [HttpGet("$metadata2")] + public string Metadata2() + { + //Response.ContentType = "application/json;odata=minimalmetadata;streaming=true;charset=utf-8"; + + //return "{\"odata.metadata\":\"http://services.odata.org/V3/Northwind/Northwind.svc/$metadata\",\"value\":[{\"name\":\"People\",\"url\":\"People\"}]}"; + //Response.ContentType = "application/json;odata=minimalmetadata;streaming=true;charset=utf-8"; + //return "{\"odata.metadata\":\"http://services.odata.org/V3/Northwind/Northwind.svc/$metadata\",\"value\":[{\"name\":\"Categories\",\"url\":\"Categories\"}]}"; + Response.ContentType = "application/xml;charset=utf-8"; + + //var wc = new System.Net.WebRequest(); + + + return @" + + + + + + + + + + + + + + + + + + "; + } + + + private string SqlTypeToDemType(string sqlType) + { + switch (sqlType) + { + case "bigint": return "Edm.Int64"; + case "binary": return "Edm.Byte[]"; + case "bit": return "Edm.Boolean"; + case "char": return "Edm.String"; + case "date": return "Edm.DateTime"; + case "datetime": return "Edm.DateTime"; + case "datetime2": return "Edm.DateTime"; + case "datetimeoffset": return "Edm.DateTimeOffset"; + case "decimal": return "Edm.Decimal"; + case "float": return "Edm.Double"; + case "image": return "Edm.Byte[]"; + case "int": return "Edm.Int32"; + case "money": return "Edm.Decimal"; + case "nchar": return "Edm.String"; + case "ntext": return "Edm.String"; + case "numeric": return "Edm.Decimal"; + case "nvarchar": return "Edm.String"; + case "real": return "Edm.Single"; + case "rowversion": return "Edm.Byte[]"; + case "smalldatetime": return "Edm.DateTime"; + case "smallint": return "Edm.Int16"; + case "smallmoney": return "Edm.Decimal"; + case "sql_variant": return "Edm.Object"; + case "text": return "Edm.String"; + case "time": return "Edm.TimeSpan"; + case "timestamp": return "Edm.Byte[]"; + case "tinyint": return "Edm.Byte"; + case "uniqueidentifier": return "Edm.Guid"; + case "varbinary": return "Edm.Byte[]"; + case "varchar": return "Edm.String"; + case "xml": return "Edm.Xml"; + default: throw new ArgumentException("Unsupported type", "sqlType"); + } + } + + private string ODataMetaData(TableSpec spec, string Namespace, string EntityName) + { + var metadata = new StringBuilder(); + metadata + .AppendFormat(@" + + + ", Namespace) + .AppendFormat(@"", EntityName); + for(int i = 0; i< spec.columns.Count; i++) + { + if(i == 0) + metadata.AppendFormat(@"", spec.columns[0]); + metadata.AppendFormat(@"", spec.columns[i], "Edm.Int32"); + } + + metadata + .AppendFormat(@"") + //.Append("") + //.AppendFormat(@"") + .AppendFormat(@" + + + + ", "DefaultContainer", EntityName, "Models.Product") + .AppendFormat(@" + + "); + + return metadata.ToString(); + } + } +} \ No newline at end of file diff --git a/samples/demos/ivs-people-register/Controllers/PeopleController.cs b/samples/demos/ivs-people-register/Controllers/PeopleController.cs index 4d8bee17..14b7f9dc 100644 --- a/samples/demos/ivs-people-register/Controllers/PeopleController.cs +++ b/samples/demos/ivs-people-register/Controllers/PeopleController.cs @@ -10,7 +10,7 @@ namespace Register.Controllers public class PeopleController : Controller { IQueryPipe pipe = null; - TableSpec tableSpec = new TableSpec("dbo.People", "name,surname,address,town"); + TableSpec tableSpec = new TableSpec("dbo", "People", "name,surname,address,town"); public PeopleController(IQueryPipe sqlQueryService) { @@ -36,6 +36,7 @@ namespace Register.Controllers [HttpGet("odata")] public async Task OData() { + Response.ContentType = "application/json"; await this .ODataHandler(tableSpec, pipe) .Process(); diff --git a/samples/demos/ivs-people-register/linqpad/PeopleOData.linq b/samples/demos/ivs-people-register/linqpad/PeopleOData.linq new file mode 100644 index 00000000..5df047d3 --- /dev/null +++ b/samples/demos/ivs-people-register/linqpad/PeopleOData.linq @@ -0,0 +1,13 @@ + + + 0a98b0bc-4850-40e5-9b82-0a5339b19c5f + true + OData4.OData4DynamicDriver + http://localhost:59934/api/odata + + + +People +.Where(p=>p.id<50 && p.id>5) +.OrderBy(p=>p.town) +.Select(p=>new {p.id,p.name,p.town}) \ No newline at end of file diff --git a/samples/demos/ivs-people-register/project.json b/samples/demos/ivs-people-register/project.json index 78f44670..759b0d2e 100644 --- a/samples/demos/ivs-people-register/project.json +++ b/samples/demos/ivs-people-register/project.json @@ -16,7 +16,7 @@ "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Sql-Server-Rest-Api": "0.2.7" + "Sql-Server-Rest-Api": "0.3" }, "tools": { diff --git a/samples/demos/ivs-people-register/sql-scripts/1 setup.sql b/samples/demos/ivs-people-register/sql-scripts/1 setup.sql index e829489d..984263c8 100644 --- a/samples/demos/ivs-people-register/sql-scripts/1 setup.sql +++ b/samples/demos/ivs-people-register/sql-scripts/1 setup.sql @@ -1,4 +1,6 @@ -CREATE TABLE dbo.People( +DROP TABLE IF EXISTS dbo.People +GO +CREATE TABLE dbo.People( id int primary key identity, name nvarchar(50), surname nvarchar(50), @@ -10,10 +12,11 @@ GO alter database current set compatibility_level = 130; go +--select name, surname, address, town from dbo.People for json path declare @people nvarchar(max) = -N'[{"name":"渡邉󠄐","surname":"俊","address":"神奈川県藤沢市辻堂1-1-41","town":"辻堂"},{"name":"渡邉󠄑","surname":"龍󠄄之介","address":"神奈川県藤沢市辻󠄀堂1-503","town":"辻󠄀堂"},{"name":"渡邉󠄒","surname":"博美","address":"兵庫県芦屋市精道町7番6号","town":"芦屋"},{"name":"田辺","surname":"浩一","address":"東京都港区港南2-16-3","town":"港南"},{"name":"田辺󠄁","surname":"洋子","address":"鹿児島県薩摩川内市神田町3番22号","town":"薩摩"},{"name":"田辺󠄂","surname":"大輔","address":"愛知県名古屋市熱田区川並町2-1","town":"川並"},{"name":"斎藤","surname":"由紀","address":"滋賀県東近江市五個荘川並󠄂町1-1","town":"川並󠄂"},{"name":"齋󠄂藤","surname":"俊󠄁","address":"東京都葛󠄀飾区小岩1-1-1","town":"葛󠄀飾"},{"name":"齋󠄃藤","surname":"龍一","address":"岡山県備前市東片上126番地 ","town":"芦󠄆別市"},{"name":"龍地","surname":"花子","address":"茨城県水戸市備󠄁前町816-2","town":"芦󠄂別市"},{"name":"龍󠄃地","surname":"次郎","address":"奈良県天理市備󠄂前町2-2-2","town":"芦別市"},{"name":"伴","surname":"龍󠄅二","address":"兵庫県芦󠄀屋市精道町7番6号","town":"芦󠄂屋"}]'; +N'[{"name":"俊","surname":"渡邉󠄐","address":"神奈川県藤沢市辻堂1-1-41","town":"辻堂"},{"name":"龍󠄄之介","surname":"渡邉󠄑","address":"神奈川県藤沢市辻󠄀堂1-503","town":"辻󠄀堂"},{"name":"博美","surname":"渡邉󠄒","address":"兵庫県芦屋市精道町7番6号","town":"芦屋"},{"name":"浩一","surname":"田辺","address":"東京都港区港南2-16-3","town":"港南"},{"name":"洋子","surname":"田辺󠄁","address":"鹿児島県薩摩川内市神田町3番22号","town":"薩摩"},{"name":"大輔","surname":"田辺󠄂","address":"愛知県名古屋市熱田区川並町2-1","town":"川並"},{"name":"由紀","surname":"斎藤","address":"滋賀県東近江市五個荘川並󠄂町1-1","town":"川並󠄂"},{"name":"俊󠄁","surname":"齋󠄂藤","address":"東京都葛󠄀飾区小岩1-1-1","town":"葛󠄀飾"},{"name":"龍一","surname":"齋󠄃藤","address":"岡山県備前市東片上126番地 ","town":"芦󠄆別市"},{"name":"花子","surname":"龍地","address":"茨城県水戸市備󠄁前町816-2","town":"芦󠄂別市"},{"name":"次郎","surname":"龍󠄃地","address":"奈良県天理市備󠄂前町2-2-2","town":"芦別市"},{"name":"龍󠄅二","surname":"伴","address":"兵庫県芦󠄀屋市精道町7番6号","town":"芦󠄂屋"}]'; insert into people (name, surname, address, town) -select rtrim(name), surname, address, town +select name, surname, address, town from openjson(@people) with (name nvarchar(50),surname nvarchar(50),address nvarchar(200),town nvarchar(50)) \ No newline at end of file