From 7ae12352a655d16bfedac72276d60713269970e4 Mon Sep 17 00:00:00 2001 From: steve Date: Fri, 28 Apr 2017 11:15:52 -0500 Subject: [PATCH 1/2] [TCAPIVersion] add v1.0.3 required for current integration tests to pass --- TinCan/TCAPIVersion.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TinCan/TCAPIVersion.cs b/TinCan/TCAPIVersion.cs index cd0a80a..ee9caea 100644 --- a/TinCan/TCAPIVersion.cs +++ b/TinCan/TCAPIVersion.cs @@ -20,6 +20,7 @@ namespace TinCan { public sealed class TCAPIVersion { + public static readonly TCAPIVersion V103 = new TCAPIVersion("1.0.3"); public static readonly TCAPIVersion V102 = new TCAPIVersion("1.0.2"); public static readonly TCAPIVersion V101 = new TCAPIVersion("1.0.1"); public static readonly TCAPIVersion V100 = new TCAPIVersion("1.0.0"); @@ -41,6 +42,7 @@ public static Dictionary GetKnown() } known = new Dictionary(); + known.Add("1.0.3", V103); known.Add("1.0.2", V102); known.Add("1.0.1", V101); known.Add("1.0.0", V100); @@ -56,7 +58,8 @@ public static Dictionary GetSupported() return supported; } - supported = new Dictionary(); + supported = new Dictionary(); + supported.Add("1.0.3", V103); supported.Add("1.0.2", V102); supported.Add("1.0.1", V101); supported.Add("1.0.0", V100); From 4a677c6d2083ed38b54ea32714d140aaf45e12c3 Mon Sep 17 00:00:00 2001 From: steve Date: Fri, 28 Apr 2017 12:08:03 -0500 Subject: [PATCH 2/2] make all network LRS calls async this requires .NET Framework v4.5 to use HttpClient, and an update to NUnit 3.6.1 to support async tests. this should also allow this library to function as a PCL, as HttpClient is available while WebRequest was not. --- TinCan/ILRS.cs | 51 +-- TinCan/LRSHttpRequest.cs | 75 +++++ TinCan/LRSHttpResponse.cs | 111 +++++++ TinCan/RemoteLRS.cs | 460 ++++++++++++++------------- TinCan/TCAPIVersion.cs | 4 +- TinCan/TinCan.csproj | 13 +- TinCan/packages.config | 2 +- TinCanTests/RemoteLRSResourceTest.cs | 103 +++--- TinCanTests/TinCanTests.csproj | 16 +- TinCanTests/packages.config | 4 +- 10 files changed, 516 insertions(+), 323 deletions(-) create mode 100644 TinCan/LRSHttpRequest.cs create mode 100644 TinCan/LRSHttpResponse.cs diff --git a/TinCan/ILRS.cs b/TinCan/ILRS.cs index 9b9c176..7d145f2 100644 --- a/TinCan/ILRS.cs +++ b/TinCan/ILRS.cs @@ -15,6 +15,7 @@ limitations under the License. */ using System; using System.Collections.Generic; +using System.Threading.Tasks; using TinCan.Documents; using TinCan.LRSResponses; @@ -22,30 +23,30 @@ namespace TinCan { public interface ILRS { - AboutLRSResponse About(); - - StatementLRSResponse SaveStatement(Statement statement); - StatementLRSResponse VoidStatement(Guid id, Agent agent); - StatementsResultLRSResponse SaveStatements(List statements); - StatementLRSResponse RetrieveStatement(Guid id); - StatementLRSResponse RetrieveVoidedStatement(Guid id); - StatementsResultLRSResponse QueryStatements(StatementsQuery query); - StatementsResultLRSResponse MoreStatements(StatementsResult result); - - ProfileKeysLRSResponse RetrieveStateIds(Activity activity, Agent agent, Nullable registration = null); - StateLRSResponse RetrieveState(String id, Activity activity, Agent agent, Nullable registration = null); - LRSResponse SaveState(StateDocument state); - LRSResponse DeleteState(StateDocument state); - LRSResponse ClearState(Activity activity, Agent agent, Nullable registration = null); - - ProfileKeysLRSResponse RetrieveActivityProfileIds(Activity activity); - ActivityProfileLRSResponse RetrieveActivityProfile(String id, Activity activity); - LRSResponse SaveActivityProfile(ActivityProfileDocument profile); - LRSResponse DeleteActivityProfile(ActivityProfileDocument profile); - - ProfileKeysLRSResponse RetrieveAgentProfileIds(Agent agent); - AgentProfileLRSResponse RetrieveAgentProfile(String id, Agent agent); - LRSResponse SaveAgentProfile(AgentProfileDocument profile); - LRSResponse DeleteAgentProfile(AgentProfileDocument profile); + Task About(); + + Task SaveStatement(Statement statement); + Task VoidStatement(Guid id, Agent agent); + Task SaveStatements(List statements); + Task RetrieveStatement(Guid id); + Task RetrieveVoidedStatement(Guid id); + Task QueryStatements(StatementsQuery query); + Task MoreStatements(StatementsResult result); + + Task RetrieveStateIds(Activity activity, Agent agent, Nullable registration = null); + Task RetrieveState(String id, Activity activity, Agent agent, Nullable registration = null); + Task SaveState(StateDocument state); + Task DeleteState(StateDocument state); + Task ClearState(Activity activity, Agent agent, Nullable registration = null); + + Task RetrieveActivityProfileIds(Activity activity); + Task RetrieveActivityProfile(String id, Activity activity); + Task SaveActivityProfile(ActivityProfileDocument profile); + Task DeleteActivityProfile(ActivityProfileDocument profile); + + Task RetrieveAgentProfileIds(Agent agent); + Task RetrieveAgentProfile(String id, Agent agent); + Task SaveAgentProfile(AgentProfileDocument profile); + Task DeleteAgentProfile(AgentProfileDocument profile); } } diff --git a/TinCan/LRSHttpRequest.cs b/TinCan/LRSHttpRequest.cs new file mode 100644 index 0000000..e994236 --- /dev/null +++ b/TinCan/LRSHttpRequest.cs @@ -0,0 +1,75 @@ +/* + Copyright 2014-2017 Rustici Software + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Collections.Generic; +using System.Text; +using System.Net.Http; + +namespace TinCan +{ + /// + /// LRS HTTP request. + /// + sealed class LRSHttpRequest + { + /// + /// Gets or sets the method. + /// + /// The method. + public HttpMethod Method { get; internal set; } + + /// + /// Gets or sets the resource. + /// + /// The resource. + public string Resource { get; internal set; } + + /// + /// Gets or sets the query parameters. + /// + /// The query parameters. + public Dictionary QueryParams { get; internal set; } + + /// + /// Gets or sets the headers. + /// + /// The headers. + public Dictionary Headers { get; internal set; } + + /// + /// Gets or sets the type of the content. + /// + /// The type of the content. + public string ContentType { get; internal set; } + + /// + /// Gets or sets the content. + /// + /// The content. + public byte[] Content { get; internal set; } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return string.Format( + "[MyHTTPRequest: method={0}, resource={1}, queryParams={2}, headers={3}, contentType={4}, content={5}]", + Method, Resource, string.Join(";", QueryParams), Headers, ContentType, Encoding.UTF8.GetString(Content, 0, Content.Length)); + } + } +} diff --git a/TinCan/LRSHttpResponse.cs b/TinCan/LRSHttpResponse.cs new file mode 100644 index 0000000..f2e844c --- /dev/null +++ b/TinCan/LRSHttpResponse.cs @@ -0,0 +1,111 @@ +/* + Copyright 2014-2017 Rustici Software + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Net.Http; +using System.Text; + +namespace TinCan +{ + /// + /// LRS HTTP response. + /// + sealed class LRSHttpResponse + { + /// + /// Initializes a new instance of the class. + /// + public LRSHttpResponse() { } + + /// + /// Initializes a new instance of the class. + /// + /// Web resp. + public LRSHttpResponse(HttpResponseMessage response) + { + if (response == null) + { + throw new ArgumentNullException(nameof(response)); + } + + Status = response.StatusCode; + + if (response.Content?.Headers?.ContentType != null) + { + ContentType = response.Content.Headers.ContentType.ToString(); + } + + if (response.Headers.ETag != null) + { + Etag = response.Headers.ETag.ToString(); + } + + if (response.Content?.Headers?.LastModified != null) + { + LastModified = response.Content.Headers.LastModified.Value.LocalDateTime; + } + + Content = response.Content.ReadAsByteArrayAsync().Result; + } + + /// + /// Gets or sets the status. + /// + /// The status. + public HttpStatusCode Status { get; internal set; } + + /// + /// Gets or sets the type of the content. + /// + /// The type of the content. + public string ContentType { get; internal set; } + + /// + /// Gets or sets the content. + /// + /// The content. + public byte[] Content { get; internal set; } + + /// + /// Gets or sets the last modified. + /// + /// The last modified. + public DateTime LastModified { get; internal set; } + + /// + /// Gets or sets the etag. + /// + /// The etag. + public string Etag { get; internal set; } + + /// + /// Gets or sets the ex. + /// + /// The ex. + public Exception Exception { get; internal set; } + + /// + /// Returns a that represents the current . + /// + /// A that represents the current . + public override string ToString() + { + return string.Format("[MyHTTPResponse: Status={0}, ContentType={1}, Content={2}, LastModified={3}, Etag={4}, Exception={5}]", + Status, ContentType, Encoding.UTF8.GetString(Content, 0, Content.Length), LastModified, Etag, Exception); + } + } +} diff --git a/TinCan/RemoteLRS.cs b/TinCan/RemoteLRS.cs index af04955..85f4769 100644 --- a/TinCan/RemoteLRS.cs +++ b/TinCan/RemoteLRS.cs @@ -17,8 +17,10 @@ limitations under the License. using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text; -using System.Web; +using System.Threading.Tasks; using Newtonsoft.Json.Linq; using TinCan.Documents; using TinCan.LRSResponses; @@ -27,6 +29,7 @@ namespace TinCan { public class RemoteLRS : ILRS { + readonly HttpClient client = new HttpClient(); public Uri endpoint { get; set; } public TCAPIVersion version { get; set; } public String auth { get; set; } @@ -47,132 +50,108 @@ public RemoteLRS(Uri endpoint, TCAPIVersion version, String username, String pas public RemoteLRS(String endpoint, TCAPIVersion version, String username, String password) : this(new Uri(endpoint), version, username, password) { } public RemoteLRS(String endpoint, String username, String password) : this(endpoint, TCAPIVersion.latest(), username, password) { } - private class MyHTTPRequest + async Task MakeRequest(LRSHttpRequest req) { - public String method { get; set; } - public String resource { get; set; } - public Dictionary queryParams { get; set; } - public Dictionary headers { get; set; } - public String contentType { get; set; } - public byte[] content { get; set; } - } - - private class MyHTTPResponse - { - public HttpStatusCode status { get; set; } - public String contentType { get; set; } - public byte[] content { get; set; } - public DateTime lastModified { get; set; } - public String etag { get; set; } - public Exception ex { get; set; } - - public MyHTTPResponse() { } - public MyHTTPResponse(HttpWebResponse webResp) - { - status = webResp.StatusCode; - contentType = webResp.ContentType; - etag = webResp.Headers.Get("Etag"); - lastModified = webResp.LastModified; - - using (var stream = webResp.GetResponseStream()) - { - content = ReadFully(stream, (int)webResp.ContentLength); - } + if (req == null) + { + throw new ArgumentNullException(nameof(req)); } - } - private MyHTTPResponse MakeSyncRequest(MyHTTPRequest req) - { - String url; - if (req.resource.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) + string url; + + if (req.Resource.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) { - url = req.resource; + url = req.Resource; } else { url = endpoint.ToString(); - if (! url.EndsWith("/") && ! req.resource.StartsWith("/")) { + + if (!url.EndsWith("/", StringComparison.Ordinal) && !req.Resource.StartsWith("/", StringComparison.Ordinal)) + { url += "/"; } - url += req.resource; + + url += req.Resource; } - if (req.queryParams != null) + if (req.QueryParams != null) { - String qs = ""; - foreach (KeyValuePair entry in req.queryParams) + var qs = string.Empty; + + foreach (var entry in req.QueryParams) { - if (qs != "") + if (qs != string.Empty) { qs += "&"; } - qs += HttpUtility.UrlEncode(entry.Key) + "=" + HttpUtility.UrlEncode(entry.Value); + + qs += string.Format("{0}={1}", WebUtility.UrlEncode(entry.Key), WebUtility.UrlEncode(entry.Value)); } - if (qs != "") + + if (qs != string.Empty) { url += "?" + qs; } } // TODO: handle special properties we recognize, such as content type, modified since, etc. - var webReq = (HttpWebRequest)WebRequest.Create(url); - webReq.Method = req.method; + var webReq = new HttpRequestMessage(req.Method, new Uri(url)); + + client.DefaultRequestHeaders.Clear(); + client.DefaultRequestHeaders.Add("X-Experience-API-Version", version.ToString()); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(req.ContentType ?? "application/content-stream")); - webReq.Headers.Add("X-Experience-API-Version", version.ToString()); if (auth != null) { - webReq.Headers.Add("Authorization", auth); + client.DefaultRequestHeaders.Add("Authorization", auth); } - if (req.headers != null) + if (req.Headers != null) { - foreach (KeyValuePair entry in req.headers) + foreach (var entry in req.Headers) { - webReq.Headers.Add(entry.Key, entry.Value); + client.DefaultRequestHeaders.Add(entry.Key, entry.Value); } } - if (req.contentType != null) + if (req.Content != null) { - webReq.ContentType = req.contentType; - } - else - { - webReq.ContentType = "application/octet-stream"; - } + webReq.Content = new ByteArrayContent(req.Content); + webReq.Content.Headers.Add("Content-Length", req.Content.Length.ToString()); - if (req.content != null) - { - webReq.ContentLength = req.content.Length; - using (var stream = webReq.GetRequestStream()) + if (!string.IsNullOrWhiteSpace(req.ContentType)) { - stream.Write(req.content, 0, req.content.Length); + webReq.Content.Headers.Add("Content-Type", req.ContentType); + } + else + { + webReq.Content.Headers.Add("Content-Type", "application/json"); } } - MyHTTPResponse resp; + LRSHttpResponse resp; try { - using (var webResp = (HttpWebResponse)webReq.GetResponse()) - { - resp = new MyHTTPResponse(webResp); - } + var theResponse = await client.SendAsync(webReq).ConfigureAwait(false); + resp = new LRSHttpResponse(theResponse); } catch (WebException ex) { + resp = new LRSHttpResponse(); + if (ex.Response != null) { - using (var webResp = (HttpWebResponse)ex.Response) + using (var stream = ex.Response.GetResponseStream()) { - resp = new MyHTTPResponse(webResp); + resp.Content = ReadFully(stream, (int)ex.Response.ContentLength); } } else { - resp = new MyHTTPResponse(); - resp.content = Encoding.UTF8.GetBytes("Web exception without '.Response'"); + resp.Content = Encoding.UTF8.GetBytes("Web exception without '.Response'"); } - resp.ex = ex; + resp.Exception = ex; } return resp; @@ -231,49 +210,56 @@ private static byte[] ReadFully(Stream stream, int initialLength) return ret; } - private MyHTTPResponse GetDocument(String resource, Dictionary queryParams, Document document) + private async Task GetDocument(String resource, Dictionary queryParams, Document document) { - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = resource; - req.queryParams = queryParams; + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = resource, + QueryParams = queryParams + }; - var res = MakeSyncRequest(req); - if (res.status == HttpStatusCode.OK) + var res = await MakeRequest(req); + + if (res.Status == HttpStatusCode.OK) { - document.content = res.content; - document.contentType = res.contentType; - document.timestamp = res.lastModified; - document.etag = res.etag; + document.content = res.Content; + document.contentType = res.ContentType; + document.timestamp = res.LastModified; + document.etag = res.Etag; } return res; } - private ProfileKeysLRSResponse GetProfileKeys(String resource, Dictionary queryParams) + private async Task GetProfileKeys(String resource, Dictionary queryParams) { var r = new ProfileKeysLRSResponse(); - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = resource; - req.queryParams = queryParams; + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = resource, + QueryParams = queryParams + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } r.success = true; - var keys = JArray.Parse(Encoding.UTF8.GetString(res.content)); - if (keys.Count > 0) { + var keys = JArray.Parse(Encoding.UTF8.GetString(res.Content)); + if (keys.Count > 0) + { r.content = new List(); - foreach (JToken key in keys) { + foreach (JToken key in keys) + { r.content.Add((String)key); } } @@ -281,23 +267,25 @@ private ProfileKeysLRSResponse GetProfileKeys(String resource, Dictionary queryParams, Document document) + private async Task SaveDocument(String resource, Dictionary queryParams, Document document) { var r = new LRSResponse(); - var req = new MyHTTPRequest(); - req.method = "PUT"; - req.resource = resource; - req.queryParams = queryParams; - req.contentType = document.contentType; - req.content = document.content; + var req = new LRSHttpRequest + { + Method = HttpMethod.Put, + Resource = resource, + QueryParams = queryParams, + ContentType = document.contentType, + Content = document.content + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.NoContent) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.NoContent) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } @@ -306,21 +294,23 @@ private LRSResponse SaveDocument(String resource, Dictionary que return r; } - private LRSResponse DeleteDocument(String resource, Dictionary queryParams) + private async Task DeleteDocument(String resource, Dictionary queryParams) { var r = new LRSResponse(); - var req = new MyHTTPRequest(); - req.method = "DELETE"; - req.resource = resource; - req.queryParams = queryParams; + var req = new LRSHttpRequest + { + Method = HttpMethod.Delete, + Resource = resource, + QueryParams = queryParams + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.NoContent) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.NoContent) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } @@ -329,93 +319,100 @@ private LRSResponse DeleteDocument(String resource, Dictionary q return r; } - private StatementLRSResponse GetStatement(Dictionary queryParams) + private async Task GetStatement(Dictionary queryParams) { var r = new StatementLRSResponse(); - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = "statements"; - req.queryParams = queryParams; + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = "statements", + QueryParams = queryParams + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } r.success = true; - r.content = new Statement(new Json.StringOfJSON(Encoding.UTF8.GetString(res.content))); + r.content = new Statement(new Json.StringOfJSON(Encoding.UTF8.GetString(res.Content))); return r; } - public AboutLRSResponse About() + public async Task About() { var r = new AboutLRSResponse(); - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = "about"; + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = "about" + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } r.success = true; - r.content = new About(Encoding.UTF8.GetString(res.content)); + r.content = new About(Encoding.UTF8.GetString(res.Content)); return r; } - public StatementLRSResponse SaveStatement(Statement statement) + public async Task SaveStatement(Statement statement) { var r = new StatementLRSResponse(); - var req = new MyHTTPRequest(); - req.queryParams = new Dictionary(); - req.resource = "statements"; + var req = new LRSHttpRequest + { + QueryParams = new Dictionary(), + Resource = "statements" + }; if (statement.id == null) { - req.method = "POST"; + req.Method = HttpMethod.Post; } else { - req.method = "PUT"; - req.queryParams.Add("statementId", statement.id.ToString()); + req.Method = HttpMethod.Put; + req.QueryParams.Add("statementId", statement.id.ToString()); } - req.contentType = "application/json"; - req.content = Encoding.UTF8.GetBytes(statement.ToJSON(version)); + req.ContentType = "application/json"; + req.Content = Encoding.UTF8.GetBytes(statement.ToJSON(version)); - var res = MakeSyncRequest(req); + var res = await MakeRequest(req); if (statement.id == null) { - if (res.status != HttpStatusCode.OK) + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } - var ids = JArray.Parse(Encoding.UTF8.GetString(res.content)); + var ids = JArray.Parse(Encoding.UTF8.GetString(res.Content)); statement.id = new Guid((String)ids[0]); } - else { - if (res.status != HttpStatusCode.NoContent) + else + { + if (res.Status != HttpStatusCode.NoContent) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } } @@ -425,7 +422,7 @@ public StatementLRSResponse SaveStatement(Statement statement) return r; } - public StatementLRSResponse VoidStatement(Guid id, Agent agent) + public async Task VoidStatement(Guid id, Agent agent) { var voidStatement = new Statement { @@ -439,34 +436,36 @@ public StatementLRSResponse VoidStatement(Guid id, Agent agent) }; voidStatement.verb.display.Add("en-US", "voided"); - return SaveStatement(voidStatement); + return await SaveStatement(voidStatement); } - public StatementsResultLRSResponse SaveStatements(List statements) + public async Task SaveStatements(List statements) { var r = new StatementsResultLRSResponse(); - var req = new MyHTTPRequest(); - req.resource = "statements"; - req.method = "POST"; - req.contentType = "application/json"; + var req = new LRSHttpRequest + { + Resource = "statements", + Method = HttpMethod.Post, + ContentType = "application/json" + }; var jarray = new JArray(); foreach (Statement st in statements) { jarray.Add(st.ToJObject(version)); } - req.content = Encoding.UTF8.GetBytes(jarray.ToString()); + req.Content = Encoding.UTF8.GetBytes(jarray.ToString()); - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } - var ids = JArray.Parse(Encoding.UTF8.GetString(res.content)); + var ids = JArray.Parse(Encoding.UTF8.GetString(res.Content)); for (int i = 0; i < ids.Count; i++) { statements[i].id = new Guid((String)ids[i]); @@ -477,72 +476,77 @@ public StatementsResultLRSResponse SaveStatements(List statements) return r; } - public StatementLRSResponse RetrieveStatement(Guid id) + public async Task RetrieveStatement(Guid id) { var queryParams = new Dictionary(); queryParams.Add("statementId", id.ToString()); - return GetStatement(queryParams); + return await GetStatement(queryParams); } - public StatementLRSResponse RetrieveVoidedStatement(Guid id) + public async Task RetrieveVoidedStatement(Guid id) { var queryParams = new Dictionary(); queryParams.Add("voidedStatementId", id.ToString()); - return GetStatement(queryParams); + return await GetStatement(queryParams); } - public StatementsResultLRSResponse QueryStatements(StatementsQuery query) + public async Task QueryStatements(StatementsQuery query) { var r = new StatementsResultLRSResponse(); - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = "statements"; - req.queryParams = query.ToParameterMap(version); + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = "statements", + QueryParams = query.ToParameterMap(version) + }; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } r.success = true; - r.content = new StatementsResult(new Json.StringOfJSON(Encoding.UTF8.GetString(res.content))); + r.content = new StatementsResult(new Json.StringOfJSON(Encoding.UTF8.GetString(res.Content))); return r; } - public StatementsResultLRSResponse MoreStatements(StatementsResult result) + public async Task MoreStatements(StatementsResult result) { var r = new StatementsResultLRSResponse(); - var req = new MyHTTPRequest(); - req.method = "GET"; - req.resource = endpoint.GetLeftPart(UriPartial.Authority); - if (! req.resource.EndsWith("/")) { - req.resource += "/"; + var req = new LRSHttpRequest + { + Method = HttpMethod.Get, + Resource = endpoint.GetLeftPart(UriPartial.Authority) + }; + if (!req.Resource.EndsWith("/", StringComparison.Ordinal)) + { + req.Resource += "/"; } - req.resource += result.more; + req.Resource += result.more; - var res = MakeSyncRequest(req); - if (res.status != HttpStatusCode.OK) + var res = await MakeRequest(req); + if (res.Status != HttpStatusCode.OK) { r.success = false; - r.httpException = res.ex; - r.SetErrMsgFromBytes(res.content); + r.httpException = res.Exception; + r.SetErrMsgFromBytes(res.Content); return r; } r.success = true; - r.content = new StatementsResult(new Json.StringOfJSON(Encoding.UTF8.GetString(res.content))); + r.content = new StatementsResult(new Json.StringOfJSON(Encoding.UTF8.GetString(res.Content))); return r; } // TODO: since param - public ProfileKeysLRSResponse RetrieveStateIds(Activity activity, Agent agent, Nullable registration = null) + public async Task RetrieveStateIds(Activity activity, Agent agent, Nullable registration = null) { var queryParams = new Dictionary(); queryParams.Add("activityId", activity.id.ToString()); @@ -552,9 +556,9 @@ public ProfileKeysLRSResponse RetrieveStateIds(Activity activity, Agent agent, N queryParams.Add("registration", registration.ToString()); } - return GetProfileKeys("activities/state", queryParams); + return await GetProfileKeys("activities/state", queryParams); } - public StateLRSResponse RetrieveState(String id, Activity activity, Agent agent, Nullable registration = null) + public async Task RetrieveState(String id, Activity activity, Agent agent, Nullable registration = null) { var r = new StateLRSResponse(); @@ -574,12 +578,12 @@ public StateLRSResponse RetrieveState(String id, Activity activity, Agent agent, state.registration = registration; } - var resp = GetDocument("activities/state", queryParams, state); - if (resp.status != HttpStatusCode.OK && resp.status != HttpStatusCode.NotFound) + var resp = await GetDocument("activities/state", queryParams, state); + if (resp.Status != HttpStatusCode.OK && resp.Status != HttpStatusCode.NotFound) { r.success = false; - r.httpException = resp.ex; - r.SetErrMsgFromBytes(resp.content); + r.httpException = resp.Exception; + r.SetErrMsgFromBytes(resp.Content); return r; } r.success = true; @@ -587,7 +591,7 @@ public StateLRSResponse RetrieveState(String id, Activity activity, Agent agent, return r; } - public LRSResponse SaveState(StateDocument state) + public async Task SaveState(StateDocument state) { var queryParams = new Dictionary(); queryParams.Add("stateId", state.id); @@ -598,9 +602,9 @@ public LRSResponse SaveState(StateDocument state) queryParams.Add("registration", state.registration.ToString()); } - return SaveDocument("activities/state", queryParams, state); + return await SaveDocument("activities/state", queryParams, state); } - public LRSResponse DeleteState(StateDocument state) + public async Task DeleteState(StateDocument state) { var queryParams = new Dictionary(); queryParams.Add("stateId", state.id); @@ -611,9 +615,9 @@ public LRSResponse DeleteState(StateDocument state) queryParams.Add("registration", state.registration.ToString()); } - return DeleteDocument("activities/state", queryParams); + return await DeleteDocument("activities/state", queryParams); } - public LRSResponse ClearState(Activity activity, Agent agent, Nullable registration = null) + public async Task ClearState(Activity activity, Agent agent, Nullable registration = null) { var queryParams = new Dictionary(); queryParams.Add("activityId", activity.id.ToString()); @@ -623,18 +627,18 @@ public LRSResponse ClearState(Activity activity, Agent agent, Nullable reg queryParams.Add("registration", registration.ToString()); } - return DeleteDocument("activities/state", queryParams); + return await DeleteDocument("activities/state", queryParams); } // TODO: since param - public ProfileKeysLRSResponse RetrieveActivityProfileIds(Activity activity) + public async Task RetrieveActivityProfileIds(Activity activity) { var queryParams = new Dictionary(); queryParams.Add("activityId", activity.id.ToString()); - return GetProfileKeys("activities/profile", queryParams); + return await GetProfileKeys("activities/profile", queryParams); } - public ActivityProfileLRSResponse RetrieveActivityProfile(String id, Activity activity) + public async Task RetrieveActivityProfile(String id, Activity activity) { var r = new ActivityProfileLRSResponse(); @@ -646,12 +650,12 @@ public ActivityProfileLRSResponse RetrieveActivityProfile(String id, Activity ac profile.id = id; profile.activity = activity; - var resp = GetDocument("activities/profile", queryParams, profile); - if (resp.status != HttpStatusCode.OK && resp.status != HttpStatusCode.NotFound) + var resp = await GetDocument("activities/profile", queryParams, profile); + if (resp.Status != HttpStatusCode.OK && resp.Status != HttpStatusCode.NotFound) { r.success = false; - r.httpException = resp.ex; - r.SetErrMsgFromBytes(resp.content); + r.httpException = resp.Exception; + r.SetErrMsgFromBytes(resp.Content); return r; } r.success = true; @@ -659,33 +663,33 @@ public ActivityProfileLRSResponse RetrieveActivityProfile(String id, Activity ac return r; } - public LRSResponse SaveActivityProfile(ActivityProfileDocument profile) + public async Task SaveActivityProfile(ActivityProfileDocument profile) { var queryParams = new Dictionary(); queryParams.Add("profileId", profile.id); queryParams.Add("activityId", profile.activity.id.ToString()); - return SaveDocument("activities/profile", queryParams, profile); + return await SaveDocument("activities/profile", queryParams, profile); } - public LRSResponse DeleteActivityProfile(ActivityProfileDocument profile) + public async Task DeleteActivityProfile(ActivityProfileDocument profile) { var queryParams = new Dictionary(); queryParams.Add("profileId", profile.id); queryParams.Add("activityId", profile.activity.id.ToString()); // TODO: need to pass Etag? - return DeleteDocument("activities/profile", queryParams); + return await DeleteDocument("activities/profile", queryParams); } // TODO: since param - public ProfileKeysLRSResponse RetrieveAgentProfileIds(Agent agent) + public async Task RetrieveAgentProfileIds(Agent agent) { var queryParams = new Dictionary(); queryParams.Add("agent", agent.ToJSON(version)); - return GetProfileKeys("agents/profile", queryParams); + return await GetProfileKeys("agents/profile", queryParams); } - public AgentProfileLRSResponse RetrieveAgentProfile(String id, Agent agent) + public async Task RetrieveAgentProfile(String id, Agent agent) { var r = new AgentProfileLRSResponse(); @@ -697,12 +701,12 @@ public AgentProfileLRSResponse RetrieveAgentProfile(String id, Agent agent) profile.id = id; profile.agent = agent; - var resp = GetDocument("agents/profile", queryParams, profile); - if (resp.status != HttpStatusCode.OK && resp.status != HttpStatusCode.NotFound) + var resp = await GetDocument("agents/profile", queryParams, profile); + if (resp.Status != HttpStatusCode.OK && resp.Status != HttpStatusCode.NotFound) { r.success = false; - r.httpException = resp.ex; - r.SetErrMsgFromBytes(resp.content); + r.httpException = resp.Exception; + r.SetErrMsgFromBytes(resp.Content); return r; } r.success = true; @@ -710,22 +714,22 @@ public AgentProfileLRSResponse RetrieveAgentProfile(String id, Agent agent) return r; } - public LRSResponse SaveAgentProfile(AgentProfileDocument profile) + public async Task SaveAgentProfile(AgentProfileDocument profile) { var queryParams = new Dictionary(); queryParams.Add("profileId", profile.id); queryParams.Add("agent", profile.agent.ToJSON(version)); - return SaveDocument("agents/profile", queryParams, profile); + return await SaveDocument("agents/profile", queryParams, profile); } - public LRSResponse DeleteAgentProfile(AgentProfileDocument profile) + public async Task DeleteAgentProfile(AgentProfileDocument profile) { var queryParams = new Dictionary(); queryParams.Add("profileId", profile.id); queryParams.Add("agent", profile.agent.ToJSON(version)); // TODO: need to pass Etag? - return DeleteDocument("agents/profile", queryParams); + return await DeleteDocument("agents/profile", queryParams); } } } diff --git a/TinCan/TCAPIVersion.cs b/TinCan/TCAPIVersion.cs index ee9caea..554c98e 100644 --- a/TinCan/TCAPIVersion.cs +++ b/TinCan/TCAPIVersion.cs @@ -58,8 +58,8 @@ public static Dictionary GetSupported() return supported; } - supported = new Dictionary(); - supported.Add("1.0.3", V103); + supported = new Dictionary(); + supported.Add("1.0.3", V103); supported.Add("1.0.2", V102); supported.Add("1.0.1", V101); supported.Add("1.0.0", V100); diff --git a/TinCan/TinCan.csproj b/TinCan/TinCan.csproj index 1efe8f4..9743677 100644 --- a/TinCan/TinCan.csproj +++ b/TinCan/TinCan.csproj @@ -9,8 +9,8 @@ Properties TinCan TinCan - v3.5 512 + v4.5 true @@ -41,10 +41,6 @@ MinimumRecommendedRules.ruleset - - ..\packages\Newtonsoft.Json.8.0.3\lib\net35\Newtonsoft.Json.dll - True - @@ -52,6 +48,11 @@ + + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + + + @@ -94,6 +95,8 @@ + + diff --git a/TinCan/packages.config b/TinCan/packages.config index 7546d33..583acbd 100644 --- a/TinCan/packages.config +++ b/TinCan/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/TinCanTests/RemoteLRSResourceTest.cs b/TinCanTests/RemoteLRSResourceTest.cs index d6e0f58..bd14373 100644 --- a/TinCanTests/RemoteLRSResourceTest.cs +++ b/TinCanTests/RemoteLRSResourceTest.cs @@ -17,6 +17,7 @@ namespace TinCanTests { using System; using System.Collections.Generic; + using System.Threading.Tasks; using System.Xml; using NUnit.Framework; using Newtonsoft.Json.Linq; @@ -48,38 +49,38 @@ public void Init() } [Test] - public void TestAbout() + public async Task TestAbout() { - AboutLRSResponse lrsRes = lrs.About(); + AboutLRSResponse lrsRes = await lrs.About(); Assert.IsTrue(lrsRes.success); } [Test] - public void TestAboutFailure() + public async Task TestAboutFailure() { lrs.endpoint = new Uri("http://cloud.scorm.com/tc/3TQLAI9/sandbox/"); - AboutLRSResponse lrsRes = lrs.About(); + AboutLRSResponse lrsRes = await lrs.About(); Assert.IsFalse(lrsRes.success); Console.WriteLine("TestAboutFailure - errMsg: " + lrsRes.errMsg); } [Test] - public void TestSaveStatement() + public async Task TestSaveStatement() { var statement = new Statement(); statement.actor = Support.agent; statement.verb = Support.verb; statement.target = Support.activity; - StatementLRSResponse lrsRes = lrs.SaveStatement(statement); + StatementLRSResponse lrsRes = await lrs.SaveStatement(statement); Assert.IsTrue(lrsRes.success); Assert.AreEqual(statement, lrsRes.content); Assert.IsNotNull(lrsRes.content.id); } [Test] - public void TestSaveStatementWithID() + public async Task TestSaveStatementWithID() { var statement = new Statement(); statement.Stamp(); @@ -87,13 +88,13 @@ public void TestSaveStatementWithID() statement.verb = Support.verb; statement.target = Support.activity; - StatementLRSResponse lrsRes = lrs.SaveStatement(statement); + StatementLRSResponse lrsRes = await lrs.SaveStatement(statement); Assert.IsTrue(lrsRes.success); Assert.AreEqual(statement, lrsRes.content); } [Test] - public void TestSaveStatementStatementRef() + public async Task TestSaveStatementStatementRef() { var statement = new Statement(); statement.Stamp(); @@ -101,13 +102,13 @@ public void TestSaveStatementStatementRef() statement.verb = Support.verb; statement.target = Support.statementRef; - StatementLRSResponse lrsRes = lrs.SaveStatement(statement); + StatementLRSResponse lrsRes = await lrs.SaveStatement(statement); Assert.IsTrue(lrsRes.success); Assert.AreEqual(statement, lrsRes.content); } [Test] - public void TestSaveStatementSubStatement() + public async Task TestSaveStatementSubStatement() { var statement = new Statement(); statement.Stamp(); @@ -117,24 +118,24 @@ public void TestSaveStatementSubStatement() Console.WriteLine(statement.ToJSON(true)); - StatementLRSResponse lrsRes = lrs.SaveStatement(statement); + StatementLRSResponse lrsRes = await lrs.SaveStatement(statement); Assert.IsTrue(lrsRes.success); Assert.AreEqual(statement, lrsRes.content); } [Test] - public void TestVoidStatement() + public async Task TestVoidStatement() { Guid toVoid = Guid.NewGuid(); - StatementLRSResponse lrsRes = lrs.VoidStatement(toVoid, Support.agent); + StatementLRSResponse lrsRes = await lrs.VoidStatement(toVoid, Support.agent); Assert.IsTrue(lrsRes.success, "LRS response successful"); Assert.AreEqual(new Uri("http://adlnet.gov/expapi/verbs/voided"), lrsRes.content.verb.id, "voiding statement uses voided verb"); - Assert.AreEqual(toVoid, ((StatementRef) lrsRes.content.target).id, "voiding statement target correct id"); + Assert.AreEqual(toVoid, ((StatementRef)lrsRes.content.target).id, "voiding statement target correct id"); } [Test] - public void TestSaveStatements() + public async Task TestSaveStatements() { var statement1 = new Statement(); statement1.actor = Support.agent; @@ -151,13 +152,13 @@ public void TestSaveStatements() statements.Add(statement1); statements.Add(statement2); - StatementsResultLRSResponse lrsRes = lrs.SaveStatements(statements); + StatementsResultLRSResponse lrsRes = await lrs.SaveStatements(statements); Assert.IsTrue(lrsRes.success); // TODO: check statements match and ids not null } [Test] - public void TestRetrieveStatement() + public async Task TestRetrieveStatement() { var statement = new TinCan.Statement(); statement.Stamp(); @@ -167,10 +168,10 @@ public void TestRetrieveStatement() statement.context = Support.context; statement.result = Support.result; - StatementLRSResponse saveRes = lrs.SaveStatement(statement); + StatementLRSResponse saveRes = await lrs.SaveStatement(statement); if (saveRes.success) { - StatementLRSResponse retRes = lrs.RetrieveStatement(saveRes.content.id.Value); + StatementLRSResponse retRes = await lrs.RetrieveStatement(saveRes.content.id.Value); Assert.IsTrue(retRes.success); Console.WriteLine("TestRetrieveStatement - statement: " + retRes.content.ToJSON(true)); } @@ -181,7 +182,7 @@ public void TestRetrieveStatement() } [Test] - public void TestQueryStatements() + public async Task TestQueryStatements() { var query = new TinCan.StatementsQuery(); query.agent = Support.agent; @@ -192,22 +193,22 @@ public void TestQueryStatements() query.format = StatementsQueryResultFormat.IDS; query.limit = 10; - StatementsResultLRSResponse lrsRes = lrs.QueryStatements(query); + StatementsResultLRSResponse lrsRes = await lrs.QueryStatements(query); Assert.IsTrue(lrsRes.success); Console.WriteLine("TestQueryStatements - statement count: " + lrsRes.content.statements.Count); } [Test] - public void TestMoreStatements() + public async Task TestMoreStatements() { var query = new TinCan.StatementsQuery(); query.format = StatementsQueryResultFormat.IDS; query.limit = 2; - StatementsResultLRSResponse queryRes = lrs.QueryStatements(query); + StatementsResultLRSResponse queryRes = await lrs.QueryStatements(query); if (queryRes.success && queryRes.content.more != null) { - StatementsResultLRSResponse moreRes = lrs.MoreStatements(queryRes.content); + StatementsResultLRSResponse moreRes = await lrs.MoreStatements(queryRes.content); Assert.IsTrue(moreRes.success); Console.WriteLine("TestMoreStatements - statement count: " + moreRes.content.statements.Count); } @@ -218,22 +219,22 @@ public void TestMoreStatements() } [Test] - public void TestRetrieveStateIds() + public async Task TestRetrieveStateIds() { - ProfileKeysLRSResponse lrsRes = lrs.RetrieveStateIds(Support.activity, Support.agent); + ProfileKeysLRSResponse lrsRes = await lrs.RetrieveStateIds(Support.activity, Support.agent); Assert.IsTrue(lrsRes.success); } [Test] - public void TestRetrieveState() + public async Task TestRetrieveState() { - StateLRSResponse lrsRes = lrs.RetrieveState("test", Support.activity, Support.agent); + StateLRSResponse lrsRes = await lrs.RetrieveState("test", Support.activity, Support.agent); Assert.IsTrue(lrsRes.success); Assert.IsInstanceOf(lrsRes.content); } [Test] - public void TestSaveState() + public async Task TestSaveState() { var doc = new StateDocument(); doc.activity = Support.activity; @@ -241,102 +242,102 @@ public void TestSaveState() doc.id = "test"; doc.content = System.Text.Encoding.UTF8.GetBytes("Test value"); - LRSResponse lrsRes = lrs.SaveState(doc); + LRSResponse lrsRes = await lrs.SaveState(doc); Assert.IsTrue(lrsRes.success); } [Test] - public void TestDeleteState() + public async Task TestDeleteState() { var doc = new StateDocument(); doc.activity = Support.activity; doc.agent = Support.agent; doc.id = "test"; - LRSResponse lrsRes = lrs.DeleteState(doc); + LRSResponse lrsRes = await lrs.DeleteState(doc); Assert.IsTrue(lrsRes.success); } [Test] - public void TestClearState() + public async Task TestClearState() { - LRSResponse lrsRes = lrs.ClearState(Support.activity, Support.agent); + LRSResponse lrsRes = await lrs.ClearState(Support.activity, Support.agent); Assert.IsTrue(lrsRes.success); } [Test] - public void TestRetrieveActivityProfileIds() + public async Task TestRetrieveActivityProfileIds() { - ProfileKeysLRSResponse lrsRes = lrs.RetrieveActivityProfileIds(Support.activity); + ProfileKeysLRSResponse lrsRes = await lrs.RetrieveActivityProfileIds(Support.activity); Assert.IsTrue(lrsRes.success); } [Test] - public void TestRetrieveActivityProfile() + public async Task TestRetrieveActivityProfile() { - ActivityProfileLRSResponse lrsRes = lrs.RetrieveActivityProfile("test", Support.activity); + ActivityProfileLRSResponse lrsRes = await lrs.RetrieveActivityProfile("test", Support.activity); Assert.IsTrue(lrsRes.success); Assert.IsInstanceOf(lrsRes.content); } [Test] - public void TestSaveActivityProfile() + public async Task TestSaveActivityProfile() { var doc = new ActivityProfileDocument(); doc.activity = Support.activity; doc.id = "test"; doc.content = System.Text.Encoding.UTF8.GetBytes("Test value"); - LRSResponse lrsRes = lrs.SaveActivityProfile(doc); + LRSResponse lrsRes = await lrs.SaveActivityProfile(doc); Assert.IsTrue(lrsRes.success); } [Test] - public void TestDeleteActivityProfile() + public async Task TestDeleteActivityProfile() { var doc = new ActivityProfileDocument(); doc.activity = Support.activity; doc.id = "test"; - LRSResponse lrsRes = lrs.DeleteActivityProfile(doc); + LRSResponse lrsRes = await lrs.DeleteActivityProfile(doc); Assert.IsTrue(lrsRes.success); } [Test] - public void TestRetrieveAgentProfileIds() + public async Task TestRetrieveAgentProfileIds() { - ProfileKeysLRSResponse lrsRes = lrs.RetrieveAgentProfileIds(Support.agent); + ProfileKeysLRSResponse lrsRes = await lrs.RetrieveAgentProfileIds(Support.agent); Assert.IsTrue(lrsRes.success); } [Test] - public void TestRetrieveAgentProfile() + public async Task TestRetrieveAgentProfile() { - AgentProfileLRSResponse lrsRes = lrs.RetrieveAgentProfile("test", Support.agent); + AgentProfileLRSResponse lrsRes = await lrs.RetrieveAgentProfile("test", Support.agent); Assert.IsTrue(lrsRes.success); Assert.IsInstanceOf(lrsRes.content); } [Test] - public void TestSaveAgentProfile() + public async Task TestSaveAgentProfile() { var doc = new AgentProfileDocument(); doc.agent = Support.agent; doc.id = "test"; doc.content = System.Text.Encoding.UTF8.GetBytes("Test value"); - LRSResponse lrsRes = lrs.SaveAgentProfile(doc); + LRSResponse lrsRes = await lrs.SaveAgentProfile(doc); Assert.IsTrue(lrsRes.success); } [Test] - public void TestDeleteAgentProfile() + public async Task TestDeleteAgentProfile() { var doc = new AgentProfileDocument(); doc.agent = Support.agent; doc.id = "test"; - LRSResponse lrsRes = lrs.DeleteAgentProfile(doc); + LRSResponse lrsRes = await lrs.DeleteAgentProfile(doc); Assert.IsTrue(lrsRes.success); } } diff --git a/TinCanTests/TinCanTests.csproj b/TinCanTests/TinCanTests.csproj index 348ca50..34a7347 100644 --- a/TinCanTests/TinCanTests.csproj +++ b/TinCanTests/TinCanTests.csproj @@ -9,8 +9,8 @@ Properties TinCanTests TinCanTests - v3.5 512 + v4.5 AnyCPU @@ -44,16 +44,14 @@ MinimumRecommendedRules.ruleset - - ..\packages\Newtonsoft.Json.8.0.3\lib\net35\Newtonsoft.Json.dll - True - - - ..\packages\NUnit.3.2.0\lib\net20\nunit.framework.dll - True - + + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll + diff --git a/TinCanTests/packages.config b/TinCanTests/packages.config index e1f462b..173d24f 100644 --- a/TinCanTests/packages.config +++ b/TinCanTests/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file