Skip to content

Commit 150a18d

Browse files
CG-11483 remove line brakes in bulk content on Linux (#10)
* CG-11483 remove line brakes in bulk content on Linux * CG-11483 Add tests checking exactly 2 newlines and verifying minified content in CreateContent * CG-11483 Update old tests to expect minified content instead of semi-pretty printed JSON * Add VSCode config to be able to run tests from inside the IDE * CG-11483 Refactor and change SaveContentAsync to send minified JSON instead of semi-pretty JSON
1 parent 9e5d927 commit 150a18d

File tree

3 files changed

+116
-101
lines changed

3 files changed

+116
-101
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"dotnet-test-explorer.testProjectPath":
3+
[
4+
"Optimizely.Graph.Source.Sdk.Tests/RepositoryTests/",
5+
],
6+
}

Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/RepositoryTests/GraphSourceRepositoryTests.cs

Lines changed: 91 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Optimizely.Graph.Source.Sdk.RestClientHelpers;
99
using Optimizely.Graph.Source.Sdk.SourceConfiguration;
1010
using Optimizely.Graph.Source.Sdk.Tests.ExampleObjects;
11+
using System.Text.RegularExpressions;
1112

1213
namespace Optimizely.Graph.Source.Sdk.Tests.RepositoryTests
1314
{
@@ -146,77 +147,7 @@ public async Task SaveTypesAsync_WithLink_ShouldGenerateTypesAndLink()
146147
x => x.LocationName
147148
);
148149

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-
}";
150+
var expectedJsonString = @"{""useTypedFieldNames"":true,""languages"":[],""links"":{""NameToLocationName"":{""from"":""Name$$String___searchable"",""to"":""LocationName$$String""}},""contentTypes"":{""Location"":{""contentType"":[],""properties"":{""Longitude"":{""type"":""Float"",""searchable"":false,""skip"":false},""Latitude"":{""type"":""Float"",""searchable"":false,""skip"":false},""Name"":{""type"":""String"",""searchable"":true,""skip"":false}}},""Event"":{""contentType"":[],""properties"":{""LocationName"":{""type"":""String"",""searchable"":false,""skip"":false},""Time"":{""type"":""DateTime"",""searchable"":false,""skip"":false},""Name"":{""type"":""String"",""searchable"":true,""skip"":false},""AdditionalInfo"":{""type"":""ExtraInfo""}}},""ExtraInfo"":{""contentType"":[],""properties"":{""Example1"":{""type"":""String"",""searchable"":false,""skip"":true},""Example2"":{""type"":""Int"",""searchable"":false,""skip"":false}}}},""propertyTypes"":{}}";
220151

221152
var jsonString = BuildExpectedTypeJsonString();
222153
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
@@ -264,7 +195,7 @@ public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
264195
}
265196
};
266197

267-
var expectedJsonString = BuildExpextedContentJsonString(x => x.ToString(), exampleData);
198+
var expectedJsonString = BuildExpectedContentJsonString(x => x.ToString(), exampleData);
268199

269200
var content = new StringContent(expectedJsonString, Encoding.UTF8, "application/json");
270201

@@ -348,16 +279,16 @@ public async Task SaveContentAsync_WithMultipleTypes_ShouldGenerateJsonForConten
348279
}
349280
};
350281

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 }}
282+
var expectedJsonString = @"{""index"":{""_id"":""Location-Stockholm"",""language_routing"":""en""}}
283+
{""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""}
284+
{""index"":{""_id"":""Location-London"",""language_routing"":""en""}}
285+
{""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""}
286+
{""index"":{""_id"":""Event-Future of Project Management"",""language_routing"":""en""}}
287+
{""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}}
288+
{""index"":{""_id"":""Event-Week of Hope: Football Camp for Homeless Children in Hanoi!"",""language_routing"":""en""}}
289+
{""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}}
290+
{""index"":{""_id"":""Event-Optimizing Project Management: Strategies for Success"",""language_routing"":""en""}}
291+
{""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}}
361292
";
362293

363294
Func<object, string> generateId = (x) =>
@@ -374,7 +305,7 @@ public async Task SaveContentAsync_WithMultipleTypes_ShouldGenerateJsonForConten
374305

375306
};
376307

377-
var jsonString = BuildExpextedContentJsonString<object>(generateId, locationStockholm, locationLondon, event1, event2, event3);
308+
var jsonString = BuildExpectedContentJsonString<object>(generateId, locationStockholm, locationLondon, event1, event2, event3);
378309

379310
var content = new StringContent(expectedJsonString, Encoding.UTF8, "application/json");
380311

@@ -395,6 +326,78 @@ public async Task SaveContentAsync_WithMultipleTypes_ShouldGenerateJsonForConten
395326
mockRestClient.VerifyAll();
396327
}
397328

329+
[TestMethod]
330+
public async Task CreateContent_ShouldContainTwoNewLines()
331+
{
332+
// Arrange
333+
repository.ConfigureContentType<ExampleClassObject>()
334+
.Field(x => x.FirstName, IndexingType.Searchable)
335+
.Field(x => x.LastName, IndexingType.Searchable)
336+
.Field(x => x.Age, IndexingType.Queryable)
337+
.Field(x => x.SubType, IndexingType.PropertyType);
338+
339+
repository.ConfigurePropertyType<ExampleClassObject.SubType1>()
340+
.Field(x => x.One, IndexingType.Searchable)
341+
.Field(x => x.Two, IndexingType.Queryable);
342+
343+
var exampleData = new ExampleClassObject
344+
{
345+
FirstName = "First",
346+
LastName = "Last",
347+
Age = 99,
348+
SubType = new ExampleClassObject.SubType1
349+
{
350+
One = "type one",
351+
Two = 13
352+
}
353+
};
354+
355+
// Act
356+
var createdContent = repository.CreateContent(generateId: (x) => x.ToString(), exampleData);
357+
var result = createdContent.ReadAsStringAsync().Result;
358+
359+
// Assert
360+
Assert.AreEqual(2, Regex.Matches(result, "\r?\n").Count, "Expected exactly 2 windows- or unix newlines.");
361+
}
362+
363+
[TestMethod]
364+
public async Task CreateContent_ShouldProduceMinifiedContent()
365+
{
366+
// Arrange
367+
repository.ConfigureContentType<ExampleClassObject>()
368+
.Field(x => x.FirstName, IndexingType.Searchable)
369+
.Field(x => x.LastName, IndexingType.Searchable)
370+
.Field(x => x.Age, IndexingType.Queryable)
371+
.Field(x => x.SubType, IndexingType.PropertyType);
372+
373+
repository.ConfigurePropertyType<ExampleClassObject.SubType1>()
374+
.Field(x => x.One, IndexingType.Searchable)
375+
.Field(x => x.Two, IndexingType.Queryable);
376+
377+
var exampleData = new ExampleClassObject
378+
{
379+
FirstName = "First",
380+
LastName = "Last",
381+
Age = 99,
382+
SubType = new ExampleClassObject.SubType1
383+
{
384+
One = "type one",
385+
Two = 13
386+
}
387+
};
388+
389+
// Act
390+
var createdContent = repository.CreateContent(generateId: (x) => x.ToString(), exampleData);
391+
var result = createdContent.ReadAsStringAsync().Result;
392+
393+
// Assert
394+
var lines = result.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
395+
Assert.AreEqual(3, lines.Length, "Expected exactly 3 lines (1 index, 1 content and 1 empty).");
396+
Assert.AreEqual("""{"index":{"_id":"Optimizely.Graph.Source.Sdk.Tests.ExampleObjects.ExampleClassObject","language_routing":"en"}}""", lines[0], "Expected index line to be minified (to not contain spaces).");
397+
Assert.AreEqual("""{"Status$$String":"Published","__typename":"ExampleClassObject","_rbac":"r:Everyone:Read","ContentType$$String":["ExampleClassObject"],"Language":{"Name$$String":"en"},"FirstName$$String___searchable":"First","LastName$$String___searchable":"Last","Age$$Int":99,"SubType":{"One$$String___searchable":"type one","Two$$Int":13}}""", lines[1], "Expected content line to be minified (to not contain spaces).");
398+
Assert.AreEqual("", lines[2], "Expected empty line to be empty.");
399+
}
400+
398401
[TestMethod]
399402
public async Task DeleteContentAsync_ThrowsNotImplementedException()
400403
{
@@ -419,7 +422,7 @@ private string BuildExpectedTypeJsonString()
419422
{
420423
var serializeOptions = new JsonSerializerOptions
421424
{
422-
WriteIndented = true,
425+
WriteIndented = false,
423426
Converters =
424427
{
425428
new SourceSdkContentTypeConverter()
@@ -429,11 +432,11 @@ private string BuildExpectedTypeJsonString()
429432
return JsonSerializer.Serialize(SourceConfigurationModel.GetTypeFieldConfiguration(), serializeOptions);
430433
}
431434

432-
private string BuildExpextedContentJsonString<T>(Func<T, string> generateId, params T[] items)
435+
private string BuildExpectedContentJsonString<T>(Func<T, string> generateId, params T[] items)
433436
{
434437
var serializeOptions = new JsonSerializerOptions
435438
{
436-
WriteIndented = true,
439+
WriteIndented = false,
437440
Converters =
438441
{
439442
new SourceSdkContentConverter()
@@ -444,9 +447,9 @@ private string BuildExpextedContentJsonString<T>(Func<T, string> generateId, par
444447

445448
foreach (var data in items)
446449
{
447-
itemJson += $"{{ \"index\": {{ \"_id\": \"{generateId(data)}\", \"language_routing\": \"en\" }} }}";
450+
itemJson += $"{{\"index\":{{\"_id\":\"{generateId(data)}\",\"language_routing\":\"en\"}}}}";
448451
itemJson += Environment.NewLine;
449-
itemJson += JsonSerializer.Serialize(data, serializeOptions).Replace("\r\n", "");
452+
itemJson += JsonSerializer.Serialize(data, serializeOptions);
450453
itemJson += Environment.NewLine;
451454
}
452455
return itemJson;

Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk/Repositories/GraphSourceRepository.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,25 @@ public async Task<string> SaveTypesAsync()
8282
/// <inheritdoc/>
8383
public async Task<string> SaveContentAsync<T>(Func<T, string> generateId, params T[] data)
8484
where T : class, new()
85+
{
86+
var content = CreateContent(generateId, data);
87+
88+
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{DataUrl}?id={source}"))
89+
{
90+
requestMessage.Content = content;
91+
using (var responseMessage = await client.SendAsync(requestMessage))
92+
{
93+
await client.HandleResponse(responseMessage);
94+
}
95+
}
96+
return string.Empty;
97+
}
98+
99+
public StringContent CreateContent<T>(Func<T, string> generateId, params T[] data)
85100
{
86101
var serializeOptions = new JsonSerializerOptions
87102
{
88-
WriteIndented = true,
103+
WriteIndented = false,
89104
Converters =
90105
{
91106
new SourceSdkContentConverter()
@@ -98,23 +113,14 @@ public async Task<string> SaveContentAsync<T>(Func<T, string> generateId, params
98113
var id = generateId(item);
99114
var language = "en";
100115

101-
itemJson += $"{{ \"index\": {{ \"_id\": \"{id}\", \"language_routing\": \"{language}\" }} }}";
116+
itemJson += $"{{\"index\":{{\"_id\":\"{id}\",\"language_routing\":\"{language}\"}}}}";
102117
itemJson += Environment.NewLine;
103-
itemJson += JsonSerializer.Serialize(item, serializeOptions).Replace("\r\n", "");
118+
itemJson += JsonSerializer.Serialize(item, serializeOptions);
104119
itemJson += Environment.NewLine;
105120
}
106121

107122
var content = new StringContent(itemJson, Encoding.UTF8, "application/json");
108-
109-
using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{DataUrl}?id={source}"))
110-
{
111-
requestMessage.Content = content;
112-
using (var responseMessage = await client.SendAsync(requestMessage))
113-
{
114-
await client.HandleResponse(responseMessage);
115-
}
116-
}
117-
return string.Empty;
123+
return content;
118124
}
119125

120126
/// <inheritdoc/>

0 commit comments

Comments
 (0)