77using System . Text ;
88using Optimizely . Graph . Source . Sdk . RestClientHelpers ;
99using Optimizely . Graph . Source . Sdk . SourceConfiguration ;
10+ using Optimizely . Graph . Source . Sdk . Tests . ExampleObjects ;
1011
1112namespace Optimizely . Graph . Source . Sdk . Tests . RepositoryTests
1213{
@@ -24,6 +25,12 @@ public GraphSourceRepositoryTests()
2425 repository = new GraphSourceRepository ( mockRestClient . Object , source ) ;
2526 }
2627
28+ [ TestInitialize ]
29+ public void TestInit ( )
30+ {
31+ SourceConfigurationModel . Reset ( ) ;
32+ }
33+
2734 [ TestMethod ]
2835 public void Constructor_WithEmptySource_ThrowsArgumentNullException ( )
2936 {
@@ -114,6 +121,123 @@ public async Task SaveTypesAsync_BuildsExpectedJsonString_AndCallsGraphClient()
114121 mockRestClient . VerifyAll ( ) ;
115122 }
116123
124+ [ TestMethod ]
125+ public async Task SaveTypesAsync_WithLink_ShouldGenerateTypesAndLink ( )
126+ {
127+ // Arrange
128+ repository . ConfigureContentType < Location > ( )
129+ . Field ( x => x . Longitude , IndexingType . Queryable )
130+ . Field ( x => x . Latitude , IndexingType . Queryable )
131+ . Field ( x => x . Name , IndexingType . Searchable ) ;
132+
133+ repository . ConfigureContentType < Event > ( )
134+ . Field ( x => x . LocationName , IndexingType . Queryable )
135+ . Field ( x => x . Time , IndexingType . Queryable )
136+ . Field ( x => x . Name , IndexingType . Searchable )
137+ . Field ( x => x . AdditionalInfo , IndexingType . PropertyType ) ;
138+
139+ repository . ConfigureContentType < ExtraInfo > ( )
140+ . Field ( x => x . Example1 , IndexingType . OnlyStored )
141+ . Field ( x => x . Example2 , IndexingType . Queryable ) ;
142+
143+ repository . ConfigureLink < Location , Event > (
144+ "NameToLocationName" ,
145+ x => x . Name ,
146+ x => x . LocationName
147+ ) ;
148+
149+ var expectedJsonString = @"{
150+ ""useTypedFieldNames"": true,
151+ ""languages"": [],
152+ ""links"": {
153+ ""NameToLocationName"": {
154+ ""from"": ""Name$$String___searchable"",
155+ ""to"": ""LocationName$$String""
156+ }
157+ },
158+ ""contentTypes"": {
159+ ""Location"": {
160+ ""contentType"": [],
161+ ""properties"": {
162+ ""Longitude"": {
163+ ""type"": ""Float"",
164+ ""searchable"": false,
165+ ""skip"": false
166+ },
167+ ""Latitude"": {
168+ ""type"": ""Float"",
169+ ""searchable"": false,
170+ ""skip"": false
171+ },
172+ ""Name"": {
173+ ""type"": ""String"",
174+ ""searchable"": true,
175+ ""skip"": false
176+ }
177+ }
178+ },
179+ ""Event"": {
180+ ""contentType"": [],
181+ ""properties"": {
182+ ""LocationName"": {
183+ ""type"": ""String"",
184+ ""searchable"": false,
185+ ""skip"": false
186+ },
187+ ""Time"": {
188+ ""type"": ""DateTime"",
189+ ""searchable"": false,
190+ ""skip"": false
191+ },
192+ ""Name"": {
193+ ""type"": ""String"",
194+ ""searchable"": true,
195+ ""skip"": false
196+ },
197+ ""AdditionalInfo"": {
198+ ""type"": ""ExtraInfo""
199+ }
200+ }
201+ },
202+ ""ExtraInfo"": {
203+ ""contentType"": [],
204+ ""properties"": {
205+ ""Example1"": {
206+ ""type"": ""String"",
207+ ""searchable"": false,
208+ ""skip"": true
209+ },
210+ ""Example2"": {
211+ ""type"": ""Int"",
212+ ""searchable"": false,
213+ ""skip"": false
214+ }
215+ }
216+ }
217+ },
218+ ""propertyTypes"": {}
219+ }" ;
220+
221+ var jsonString = BuildExpectedTypeJsonString ( ) ;
222+ var content = new StringContent ( jsonString , Encoding . UTF8 , "application/json" ) ;
223+
224+ var response = new HttpResponseMessage ( HttpStatusCode . OK ) ;
225+ var request = new HttpRequestMessage ( HttpMethod . Put , $ "/api/content/v3/types?id={ source } ") { Content = content } ;
226+
227+ mockRestClient . Setup ( c => c . SendAsync ( It . IsAny < HttpRequestMessage > ( ) ) ) . ReturnsAsync ( response ) ;
228+ mockRestClient . Setup ( c => c . HandleResponse ( response ) ) ;
229+
230+ // Act
231+ await repository . SaveTypesAsync ( ) ;
232+
233+ // Assert
234+ Assert . AreEqual ( expectedJsonString , jsonString ) ;
235+
236+ mockRestClient . Verify ( c => c . SendAsync ( It . Is < HttpRequestMessage > ( x => Compare ( request , x ) ) ) , Times . Once ) ;
237+ mockRestClient . Verify ( c => c . HandleResponse ( response ) , Times . Once ) ;
238+ mockRestClient . VerifyAll ( ) ;
239+ }
240+
117241 [ TestMethod ]
118242 public async Task SaveContentAsync_SerializesData_AndCallsGraphClient ( )
119243 {
@@ -140,7 +264,7 @@ public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
140264 }
141265 } ;
142266
143- var expectedJsonString = BuildExpextedContentJsonString ( exampleData ) ;
267+ var expectedJsonString = BuildExpextedContentJsonString ( x => x . ToString ( ) , exampleData ) ;
144268
145269 var content = new StringContent ( expectedJsonString , Encoding . UTF8 , "application/json" ) ;
146270
@@ -159,6 +283,118 @@ public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
159283 mockRestClient . VerifyAll ( ) ;
160284 }
161285
286+ [ TestMethod ]
287+ public async Task SaveContentAsync_WithMultipleTypes_ShouldGenerateJsonForContentOfDifferentTypes ( )
288+ {
289+ // Arrange
290+ repository . ConfigureContentType < Location > ( )
291+ . Field ( x => x . Longitude , IndexingType . Queryable )
292+ . Field ( x => x . Latitude , IndexingType . Queryable )
293+ . Field ( x => x . Name , IndexingType . Searchable ) ;
294+
295+ repository . ConfigureContentType < Event > ( )
296+ . Field ( x => x . LocationName , IndexingType . Queryable )
297+ . Field ( x => x . Time , IndexingType . Queryable )
298+ . Field ( x => x . Name , IndexingType . Searchable )
299+ . Field ( x => x . AdditionalInfo , IndexingType . PropertyType ) ;
300+
301+ repository . ConfigureContentType < ExtraInfo > ( )
302+ . Field ( x => x . Example1 , IndexingType . OnlyStored )
303+ . Field ( x => x . Example2 , IndexingType . Queryable ) ;
304+
305+ var locationStockholm = new Location
306+ {
307+ Name = "Stockholm" ,
308+ Latitude = 59.334591 ,
309+ Longitude = 18.063241 ,
310+ } ;
311+ var locationLondon = new Location
312+ {
313+ Name = "London" ,
314+ Latitude = 51.5072 ,
315+ Longitude = 0.1275 ,
316+ } ;
317+ var event1 = new Event
318+ {
319+ Name = "Future of Project Management" ,
320+ Time = new DateTime ( 2024 , 10 , 22 ) ,
321+ LocationName = "Stockholm" ,
322+ AdditionalInfo = new ExtraInfo
323+ {
324+ Example1 = "test1" ,
325+ Example2 = 1
326+ }
327+ } ;
328+ var event2 = new Event
329+ {
330+ Name = "Week of Hope: Football Camp for Homeless Children in Hanoi!" ,
331+ Time = new DateTime ( 2024 , 10 , 27 ) ,
332+ LocationName = "Hanoi" ,
333+ AdditionalInfo = new ExtraInfo
334+ {
335+ Example1 = "test2" ,
336+ Example2 = 2
337+ }
338+ } ;
339+ var event3 = new Event
340+ {
341+ Name = "Optimizing Project Management: Strategies for Success" ,
342+ Time = new DateTime ( 2024 , 11 , 03 ) ,
343+ LocationName = "London" ,
344+ AdditionalInfo = new ExtraInfo
345+ {
346+ Example1 = "test3" ,
347+ Example2 = 3
348+ }
349+ } ;
350+
351+ var expectedJsonString = @"{ ""index"": { ""_id"": ""Location-Stockholm"", ""language_routing"": ""en"" } }
352+ { ""Status$$String"": ""Published"", ""__typename"": ""Location"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Location"" ], ""Language"": { ""Name$$String"": ""en"" }, ""Longitude$$Float"": 18.063241, ""Latitude$$Float"": 59.334591, ""Name$$String___searchable"": ""Stockholm""}
353+ { ""index"": { ""_id"": ""Location-London"", ""language_routing"": ""en"" } }
354+ { ""Status$$String"": ""Published"", ""__typename"": ""Location"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Location"" ], ""Language"": { ""Name$$String"": ""en"" }, ""Longitude$$Float"": 0.1275, ""Latitude$$Float"": 51.5072, ""Name$$String___searchable"": ""London""}
355+ { ""index"": { ""_id"": ""Event-Future of Project Management"", ""language_routing"": ""en"" } }
356+ { ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""Stockholm"", ""Time$$DateTime"": ""2024-10-21T22:00:00Z"", ""Name$$String___searchable"": ""Future of Project Management"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test1"", ""Example2$$Int"": 1 }}
357+ { ""index"": { ""_id"": ""Event-Week of Hope: Football Camp for Homeless Children in Hanoi!"", ""language_routing"": ""en"" } }
358+ { ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""Hanoi"", ""Time$$DateTime"": ""2024-10-26T22:00:00Z"", ""Name$$String___searchable"": ""Week of Hope: Football Camp for Homeless Children in Hanoi!"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test2"", ""Example2$$Int"": 2 }}
359+ { ""index"": { ""_id"": ""Event-Optimizing Project Management: Strategies for Success"", ""language_routing"": ""en"" } }
360+ { ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""London"", ""Time$$DateTime"": ""2024-11-02T23:00:00Z"", ""Name$$String___searchable"": ""Optimizing Project Management: Strategies for Success"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test3"", ""Example2$$Int"": 3 }}
361+ " ;
362+
363+ Func < object , string > generateId = ( x ) =>
364+ {
365+ if ( x is Location location )
366+ {
367+ return $ "Location-{ location . Name } ";
368+ }
369+ if ( x is Event ev )
370+ {
371+ return $ "Event-{ ev . Name } ";
372+ }
373+ throw new NotImplementedException ( ) ;
374+
375+ } ;
376+
377+ var jsonString = BuildExpextedContentJsonString < object > ( generateId , locationStockholm , locationLondon , event1 , event2 , event3 ) ;
378+
379+ var content = new StringContent ( expectedJsonString , Encoding . UTF8 , "application/json" ) ;
380+
381+ var response = new HttpResponseMessage ( HttpStatusCode . OK ) ;
382+ var request = new HttpRequestMessage ( HttpMethod . Post , $ "/api/content/v2/data?id={ source } ") { Content = content } ;
383+
384+ mockRestClient . Setup ( c => c . SendAsync ( It . IsAny < HttpRequestMessage > ( ) ) ) . ReturnsAsync ( response ) ;
385+ mockRestClient . Setup ( c => c . HandleResponse ( response ) ) ;
386+
387+ // Act
388+ await repository . SaveContentAsync < object > ( generateId , locationStockholm , locationLondon , event1 , event2 , event3 ) ;
389+
390+ // Assert
391+ Assert . AreEqual ( expectedJsonString , jsonString ) ;
392+
393+ mockRestClient . Verify ( c => c . SendAsync ( It . Is < HttpRequestMessage > ( x => Compare ( request , x ) ) ) , Times . Once ) ;
394+ mockRestClient . Verify ( c => c . HandleResponse ( response ) , Times . Once ) ;
395+ mockRestClient . VerifyAll ( ) ;
396+ }
397+
162398 [ TestMethod ]
163399 public async Task DeleteContentAsync_ThrowsNotImplementedException ( )
164400 {
@@ -177,6 +413,7 @@ public async Task DeleteContentAsync_ThrowsNotImplementedException()
177413 mockRestClient . VerifyAll ( ) ;
178414 }
179415
416+
180417 #region Private
181418 private string BuildExpectedTypeJsonString ( )
182419 {
@@ -192,7 +429,7 @@ private string BuildExpectedTypeJsonString()
192429 return JsonSerializer . Serialize ( SourceConfigurationModel . GetTypeFieldConfiguration ( ) , serializeOptions ) ;
193430 }
194431
195- private string BuildExpextedContentJsonString ( ExampleClassObject data )
432+ private string BuildExpextedContentJsonString < T > ( Func < T , string > generateId , params T [ ] items )
196433 {
197434 var serializeOptions = new JsonSerializerOptions
198435 {
@@ -205,11 +442,13 @@ private string BuildExpextedContentJsonString(ExampleClassObject data)
205442
206443 var itemJson = string . Empty ;
207444
208- itemJson += $ "{{ \" index\" : {{ \" _id\" : \" Optimizely.Graph.Source.Sdk.Tests.ExampleClassObject\" , \" language_routing\" : \" en\" }} }}";
209- itemJson += Environment . NewLine ;
210- itemJson += JsonSerializer . Serialize ( data , serializeOptions ) . Replace ( "\r \n " , "" ) ;
211- itemJson += Environment . NewLine ;
212-
445+ foreach ( var data in items )
446+ {
447+ itemJson += $ "{{ \" index\" : {{ \" _id\" : \" { generateId ( data ) } \" , \" language_routing\" : \" en\" }} }}";
448+ itemJson += Environment . NewLine ;
449+ itemJson += JsonSerializer . Serialize ( data , serializeOptions ) . Replace ( "\r \n " , "" ) ;
450+ itemJson += Environment . NewLine ;
451+ }
213452 return itemJson ;
214453 }
215454
0 commit comments