@@ -20,6 +20,7 @@ limitations under the License.
2020using System . IO ;
2121using System . Linq ;
2222using System . Net ;
23+ using System . Net . Http ;
2324using System . Text ;
2425using System . Threading . Tasks ;
2526using System . Web ;
@@ -28,18 +29,25 @@ limitations under the License.
2829
2930namespace TinCan
3031{
31- public class RemoteLrs : ILrs
32+ public class RemoteLrs : IRemoteLrs
3233 {
3334 public Uri Endpoint { get ; set ; }
3435 public TCAPIVersion Version { get ; set ; }
3536 public string Auth { get ; set ; }
3637 public Dictionary < string , string > Extended { get ; set ; } = new Dictionary < string , string > ( ) ;
3738 public Dictionary < string , string > Headers { get ; set ; } = new Dictionary < string , string > ( ) ;
39+ private HttpClient _httpClient { get ; set ; }
40+ public bool UseHttpClient { get ; set ; }
3841
3942 public RemoteLrs ( )
4043 {
4144 }
4245
46+ public RemoteLrs ( HttpClient httpClient )
47+ {
48+ _httpClient = httpClient ;
49+ }
50+
4351 public RemoteLrs ( Uri endpoint , TCAPIVersion version , string username , string password )
4452 {
4553 Endpoint = endpoint ;
@@ -60,19 +68,23 @@ public RemoteLrs(string endpoint, string username, string password) : this(endpo
6068 private class MyHttpRequest
6169 {
6270 public string Method { get ; set ; }
71+ public HttpMethod HttpMethod { get ; set ; }
6372 public string Resource { get ; set ; }
6473 public Dictionary < string , string > QueryParams { get ; set ; } = new Dictionary < string , string > ( ) ;
6574 public Dictionary < string , string > Headers { get ; set ; } = new Dictionary < string , string > ( ) ;
6675
6776 public string ContentType { get ; set ; }
6877 public byte [ ] Content { get ; set ; }
78+ public string StringContent { get ; set ; }
79+
6980 }
7081
7182 private class MyHttpResponse
7283 {
7384 public HttpStatusCode Status { get ; }
7485 public string ContentType { get ; }
7586 public byte [ ] Content { get ; set ; }
87+ public string StringContent { get ; set ; }
7688 public DateTime LastModified { get ; }
7789 public string Etag { get ; }
7890 public Exception Ex { get ; set ; }
@@ -101,6 +113,34 @@ public MyHttpResponse(HttpWebResponse webResp)
101113 Content = ReadFully ( stream , ( int ) webResp . ContentLength ) ;
102114 }
103115 }
116+
117+ public MyHttpResponse ( HttpResponseMessage httpResponseMessage )
118+
119+ {
120+ Status = httpResponseMessage . StatusCode ;
121+ try
122+ {
123+ if ( httpResponseMessage . Headers . Contains ( "Etag" ) )
124+ {
125+ Etag = httpResponseMessage . Headers . GetValues ( "Etag" ) ? . FirstOrDefault ( ) ? . ToString ( ) ;
126+ }
127+
128+ if ( httpResponseMessage . Content . Headers . TryGetValues ( "LastModified" , out var values ) )
129+ {
130+ // Get the Last-Modified header value and parse it to DateTime
131+ var lastModifiedString = values . ToString ( ) ;
132+
133+ if ( DateTime . TryParse ( lastModifiedString , out DateTime lastModifiedDate ) )
134+ {
135+ LastModified = lastModifiedDate ;
136+ }
137+ }
138+ }
139+ catch
140+ {
141+ //sometimes will throw an exception, just ignore
142+ }
143+ }
104144 }
105145
106146 private async Task < MyHttpResponse > MakeRequest ( MyHttpRequest req )
@@ -158,6 +198,68 @@ private async Task<MyHttpResponse> MakeRequest(MyHttpRequest req)
158198 return resp ;
159199 }
160200
201+ private async Task < MyHttpResponse > MakeHttpRequest ( MyHttpRequest req )
202+ {
203+ var httpRequestMessage = BuildHttpRequestMessage ( req ) ;
204+
205+ try
206+ {
207+ if ( _httpClient == null ) _httpClient = new HttpClient ( ) ;
208+ var httpResponseMessage = await _httpClient . SendAsync ( httpRequestMessage ) ;
209+ var response = new MyHttpResponse ( httpResponseMessage ) ;
210+ response . StringContent = await httpResponseMessage . Content . ReadAsStringAsync ( ) ;
211+ return response ;
212+
213+ }
214+ catch ( HttpRequestException ex )
215+ {
216+ var httpResponseMessage = new HttpResponseMessage ( )
217+ {
218+ StatusCode = ex . StatusCode . Value ,
219+ Content = new StringContent ( $ "Request failed: { ex . Message } ") ,
220+ } ;
221+
222+ var response = new MyHttpResponse ( httpResponseMessage ) ;
223+ response . Ex = ex ;
224+ return response ;
225+ }
226+ catch ( Exception ex )
227+ {
228+
229+ var httpResponseMessage = new HttpResponseMessage ( )
230+ {
231+ StatusCode = HttpStatusCode . InternalServerError ,
232+ Content = new StringContent ( $ "Request failed: { ex . Message } ") ,
233+ } ;
234+
235+ var response = new MyHttpResponse ( httpResponseMessage ) ;
236+ response . Ex = ex ;
237+ return response ;
238+ }
239+ }
240+
241+ private HttpRequestMessage BuildHttpRequestMessage ( MyHttpRequest req )
242+ {
243+ string url = GetEndpointUrl ( req . Resource ) ;
244+ url = AppendQueryStringParamsToUrl ( url , req . QueryParams ) ;
245+
246+ var httpRequestMessage = new HttpRequestMessage ( ) ;
247+ httpRequestMessage . RequestUri = new Uri ( url ) ;
248+ httpRequestMessage . Method = req . HttpMethod ;
249+
250+ AddHeadersToRequest ( req . Headers , httpRequestMessage ) ;
251+
252+ req . ContentType = req . ContentType ?? "application/octet-stream" ;
253+
254+ if ( req . StringContent != null )
255+ {
256+ httpRequestMessage . Content = new StringContent ( req . StringContent , UTF8Encoding . UTF8 , req . ContentType ) ;
257+
258+ }
259+
260+ return httpRequestMessage ;
261+ }
262+
161263 private string GetEndpointUrl ( string resource )
162264 {
163265 string url ;
@@ -207,6 +309,44 @@ private void AddHeadersToRequest(MyHttpRequest req, HttpWebRequest webReq)
207309 }
208310 }
209311
312+ private void AddHeadersToRequest ( Dictionary < string , string > requestHeaders , HttpRequestMessage httpRequestMessage )
313+ {
314+ httpRequestMessage . Headers . Clear ( ) ;
315+ httpRequestMessage . Headers . Add ( "X-Experience-API-Version" , Version . ToString ( ) ) ;
316+ if ( Auth != null )
317+ {
318+ httpRequestMessage . Headers . Add ( "Authorization" , Auth ) ;
319+ }
320+
321+ Headers . Concat ( requestHeaders ) ;
322+ foreach ( var entry in Headers )
323+ {
324+ httpRequestMessage . Headers . Add ( entry . Key , entry . Value ) ;
325+ }
326+ }
327+
328+ private StatementsResultLrsResponse BuildStatementsResultLrsResponse ( List < Statement > statements , MyHttpResponse responseMessage )
329+ {
330+ var resultLrsResponse = new StatementsResultLrsResponse ( ) ;
331+ if ( ! IsSuccessStatusCode ( responseMessage . Status ) )
332+ {
333+ resultLrsResponse . Success = false ;
334+ resultLrsResponse . HttpException = responseMessage . Ex ;
335+ resultLrsResponse . ErrMsg = responseMessage . StringContent ;
336+ return resultLrsResponse ;
337+ }
338+
339+ var ids = JArray . Parse ( responseMessage . StringContent ) ;
340+ for ( var i = 0 ; i < ids . Count ; i ++ )
341+ {
342+ statements [ i ] . Id = new Guid ( ( string ) ids [ i ] ) ;
343+ }
344+
345+ resultLrsResponse . Success = true ;
346+ resultLrsResponse . Content = new StatementsResult ( statements ) ;
347+ return resultLrsResponse ;
348+ }
349+
210350 /// <summary>
211351 /// See http://www.yoda.arachsys.com/csharp/readbinary.html no license found
212352 ///
@@ -515,47 +655,59 @@ public async Task<StatementLrsResponse> VoidStatementAsync(Guid id, Agent agent)
515655 return await SaveStatementAsync ( voidStatement ) ;
516656 }
517657
518- public async Task < StatementsResultLrsResponse > SaveStatementsAsync ( List < Statement > statements ,
519- string timestamp = null )
658+ public async Task < StatementsResultLrsResponse > SaveStatementsAsync ( List < Statement > statements )
520659 {
521- var r = new StatementsResultLrsResponse ( ) ;
522-
523- var req = new MyHttpRequest
660+ if ( UseHttpClient )
524661 {
525- Resource = "statements" ,
526- Method = "POST" ,
527- ContentType = "application/json"
528- } ;
529-
530- var jarray = new JArray ( ) ;
531- if ( ! string . IsNullOrEmpty ( timestamp ) )
532- jarray . Add ( JToken . Parse ( timestamp + '|' ) ) ;
533- foreach ( var st in statements )
534- {
535- jarray . Add ( st . ToJObject ( Version ) ) ;
662+ var req = new MyHttpRequest
663+ {
664+ Resource = "statements" ,
665+ ContentType = "application/json" ,
666+ HttpMethod = HttpMethod . Post ,
667+ StringContent = GetJsonStringFromStatements ( statements )
668+ } ;
669+ var myHttpResponse = await MakeHttpRequest ( req ) ;
670+ return BuildStatementsResultLrsResponse ( statements , myHttpResponse ) ;
536671 }
672+ else
673+ {
674+ var r = new StatementsResultLrsResponse ( ) ;
537675
538- req . Content = Encoding . UTF8 . GetBytes ( jarray . ToString ( ) ) ;
676+ var req = new MyHttpRequest
677+ {
678+ Resource = "statements" ,
679+ Method = "POST" ,
680+ ContentType = "application/json"
681+ } ;
539682
540- var res = await MakeRequest ( req ) ;
541- if ( ! IsSuccessStatusCode ( res . Status ) )
542- {
543- r . Success = false ;
544- r . HttpException = res . Ex ;
545- r . SetErrMsgFromBytes ( res . Content ) ;
546- return r ;
547- }
683+ var jarray = new JArray ( ) ;
684+ foreach ( var st in statements )
685+ {
686+ jarray . Add ( st . ToJObject ( Version ) ) ;
687+ }
548688
549- var ids = JArray . Parse ( Encoding . UTF8 . GetString ( res . Content ) ) ;
550- for ( var i = 0 ; i < ids . Count ; i ++ )
551- {
552- statements [ i ] . Id = new Guid ( ( string ) ids [ i ] ) ;
553- }
689+ req . Content = Encoding . UTF8 . GetBytes ( jarray . ToString ( ) ) ;
554690
555- r . Success = true ;
556- r . Content = new StatementsResult ( statements ) ;
691+ var res = await MakeRequest ( req ) ;
692+ if ( ! IsSuccessStatusCode ( res . Status ) )
693+ {
694+ r . Success = false ;
695+ r . HttpException = res . Ex ;
696+ r . SetErrMsgFromBytes ( res . Content ) ;
697+ return r ;
698+ }
557699
558- return r ;
700+ var ids = JArray . Parse ( Encoding . UTF8 . GetString ( res . Content ) ) ;
701+ for ( var i = 0 ; i < ids . Count ; i ++ )
702+ {
703+ statements [ i ] . Id = new Guid ( ( string ) ids [ i ] ) ;
704+ }
705+
706+ r . Success = true ;
707+ r . Content = new StatementsResult ( statements ) ;
708+
709+ return r ;
710+ }
559711 }
560712
561713 public async Task < StatementLrsResponse > RetrieveStatementAsync ( Guid id )
@@ -875,6 +1027,16 @@ public async Task<LrsResponse> DeleteAgentProfileAsync(AgentProfileDocument prof
8751027 return await DeleteDocument ( "agents/profile" , queryParams ) ;
8761028 }
8771029
1030+ public string GetJsonStringFromStatements ( List < Statement > statements )
1031+ {
1032+ var jarray = new JArray ( ) ;
1033+ foreach ( var st in statements )
1034+ {
1035+ jarray . Add ( st . ToJObject ( Version ) ) ;
1036+ }
1037+ return jarray . ToString ( ) ;
1038+ }
1039+
8781040 #endregion
8791041 }
8801042}
0 commit comments