Skip to content

Commit 1804805

Browse files
Simplify Update Entity logic (#52)
* replace name to config * symplifying update logic * adding validations * added more tests * resolved comments * adding test description * updating file names for Add and Update EntityTests * adding comments and tests * adding class comments * adding class comments * adding more tests and comments * updated accessebiity of some methods * updating tests * adding TODO for policy support * updating few comments * updated function summary * minor change * fixed comments * refactoring changes
1 parent 3939725 commit 1804805

File tree

9 files changed

+1362
-351
lines changed

9 files changed

+1362
-351
lines changed

Hawaii-Cli/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# Generated Files
77
**/bin
88
**/obj
9+
/coveragereport
10+
**/TestResults
911

1012
# Local History for Visual Studio
1113
.localhistory/

Hawaii-Cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export PATH=$PATH:~/.dotnet/tools
3333

3434
### Generate the config:
3535
```
36-
hawaii init -name <<filename>> --database-type <<db_type>> --connection-string <<connection_string>>
36+
hawaii init --name <<filename>> --database-type <<db_type>> --connection-string <<connection_string>>
3737
```
3838
### Add entity to the config:
3939
```

Hawaii-Cli/src/Models/ConfigGenerator.cs

Lines changed: 320 additions & 277 deletions
Large diffs are not rendered by default.

Hawaii-Cli/src/Models/Utils.cs

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Text.Json.Serialization;
33
using Azure.DataGateway.Config;
44
using Humanizer;
5+
using Hawaii.Cli.Models;
56
using Action = Azure.DataGateway.Config.Action;
67

78
/// <summary>
@@ -18,6 +19,8 @@ public enum CRUD
1819
}
1920
public class Utils
2021
{
22+
public const string WILDCARD = "*";
23+
2124
/// <summary>
2225
/// creates the rest object which can be either a boolean value
2326
/// or a RestEntitySettings object containing api route based on the input
@@ -83,6 +86,11 @@ public static Action GetAction(string action, string? fieldsToInclude, string? f
8386
/// </summary>
8487
public static Action? ToActionObject(JsonElement element)
8588
{
89+
if (element.ValueKind is JsonValueKind.String)
90+
{
91+
return new Action(element.GetRawText(), Policy: null, Fields: null);
92+
}
93+
8694
string json = element.GetRawText();
8795
return JsonSerializer.Deserialize<Action>(json);
8896
}
@@ -99,7 +107,7 @@ public static object[] CreateActions(string actions, string? fieldsToInclude, st
99107
return actions.Split(",");
100108
}
101109

102-
if ("*".Equals(actions))
110+
if (actions is WILDCARD)
103111
{
104112
action_items = new object[] { GetAction(actions, fieldsToInclude, fieldsToExclude) };
105113
}
@@ -134,18 +142,6 @@ public static PermissionSetting CreatePermissions(string role, string actions, s
134142
return new PermissionSetting(role, CreateActions(actions, fieldsToInclude, fieldsToExclude));
135143
}
136144

137-
/// <summary>
138-
/// Add a new PermissionSetting object(based on role, actions, fieldsToInclude, and fieldsToExclude) in the existing array of PermissionSetting.
139-
/// returns the updated array of PermissionSetting.
140-
/// </summary>
141-
public static PermissionSetting[] AddNewPermissions(PermissionSetting[] currentPermissions, string role, string actions, string? fieldsToInclude, string? fieldsToExclude)
142-
{
143-
List<PermissionSetting>? currentPermissionsList = currentPermissions.ToList();
144-
PermissionSetting? permissionSetting = CreatePermissions(role, actions, fieldsToInclude, fieldsToExclude);
145-
currentPermissionsList.Add(permissionSetting);
146-
return currentPermissionsList.ToArray();
147-
}
148-
149145
/// <summary>
150146
/// JsonNamingPolicy to convert all the keys in Json as lower case string.
151147
/// </summary>
@@ -167,8 +163,7 @@ public static JsonSerializerOptions GetSerializationOptions()
167163
{
168164
WriteIndented = true,
169165
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
170-
PropertyNamingPolicy = new LowerCaseNamingPolicy(),
171-
PropertyNameCaseInsensitive = true
166+
PropertyNamingPolicy = new LowerCaseNamingPolicy()
172167
};
173168

174169
options.Converters.Add(new JsonStringEnumConverter(namingPolicy: new LowerCaseNamingPolicy()));
@@ -180,29 +175,14 @@ public static JsonSerializerOptions GetSerializationOptions()
180175
/// </summary>
181176
public static string GetCRUDOperation(JsonElement op)
182177
{
183-
if (JsonValueKind.String.Equals(op.ValueKind))
178+
if (op.ValueKind is JsonValueKind.String)
184179
{
185180
return op.ToString();
186181
}
187182

188183
return ToActionObject(op)!.Name;
189184
}
190185

191-
/// <summary>
192-
/// returns the Cardinality from the given string(one, or many).
193-
/// </summary>
194-
public static Cardinality GetCardinalityTypeFromString(string cardinality)
195-
{
196-
if ("one".Equals(cardinality, StringComparison.OrdinalIgnoreCase))
197-
return Cardinality.One;
198-
else if ("many".Equals(cardinality, StringComparison.OrdinalIgnoreCase))
199-
return Cardinality.Many;
200-
else
201-
{
202-
throw new NotSupportedException();
203-
}
204-
}
205-
206186
/// <summary>
207187
/// returns the default global settings based on dbType.
208188
/// </summary>
@@ -234,20 +214,81 @@ public static Dictionary<GlobalSettingsType, object> GetDefaultGlobalSettings(Da
234214
// "allow-credentials": true
235215
// },
236216
// "authentication": {
237-
// "provider": "EasyAuth",
238-
// "jwt": {
239-
// "audience": "",
240-
// "issuer": "",
241-
// "issuerkey": ""
242-
// }
217+
// "provider": "EasyAuth"
243218
// }
244219
// }
245220
/// </summary>
246221
public static HostGlobalSettings GetDefaultHostGlobalSettings(HostModeType hostMode)
247222
{
248223
Cors cors = new(new string[] { });
249-
AuthenticationConfig authenticationConfig = new(Jwt: new Jwt(Audience: string.Empty, Issuer: string.Empty, IssuerKey: string.Empty));
224+
AuthenticationConfig authenticationConfig = new();
250225
return new HostGlobalSettings(hostMode, cors, authenticationConfig);
251226
}
227+
228+
/// <summary>
229+
/// Try to read and deserialize runtime config from a file.
230+
/// </summary>
231+
/// <param name="file">File path.</param>
232+
/// <param name="runtimeConfigJson">Runtime config output. On failure, this will be null.</param>
233+
/// <returns>True on success. On failure, return false and runtimeConfig will be set to null.</returns>
234+
public static bool TryReadRuntimeConfig(string file, out string runtimeConfigJson)
235+
{
236+
runtimeConfigJson = string.Empty;
237+
238+
if (!File.Exists(file))
239+
{
240+
Console.WriteLine($"ERROR: Couldn't find config file: {file}.");
241+
Console.WriteLine($"Please run: hawaii init <options> to create a new config file.");
242+
243+
return false;
244+
}
245+
246+
// Read existing config file content.
247+
//
248+
runtimeConfigJson = File.ReadAllText(file);
249+
return true;
250+
}
251+
252+
/// <summary>
253+
/// this method will parse role and Action from permission string.
254+
/// A valid permission string will be of the form "<<role>>:<<actions>>"
255+
/// it will return true if parsing is successful and add the parsed value
256+
/// to the out params role and action.
257+
/// </summary>
258+
public static bool TryGetRoleAndActionFromPermissionString(string permissions, out string? role, out string? actions)
259+
{
260+
// Split permission to role and actions
261+
//
262+
role = null;
263+
actions = null;
264+
string[] permission_array = permissions.Split(":");
265+
if (permission_array.Length != 2)
266+
{
267+
Console.WriteLine("Please add permission in the following format. --permission \"<<role>>:<<actions>>\"");
268+
return false;
269+
}
270+
271+
role = permission_array[0];
272+
actions = permission_array[1];
273+
return true;
274+
}
275+
276+
/// <summary>
277+
/// this method will write all the json string in the given file.
278+
/// </summary>
279+
public static bool WriteJsonContentToFile(string file, string jsonContent)
280+
{
281+
try
282+
{
283+
File.WriteAllText(file, jsonContent);
284+
}
285+
catch (Exception e)
286+
{
287+
Console.Error.WriteLine($"Failed to generate the config file, operation failed with exception:{e}.");
288+
return false;
289+
}
290+
291+
return true;
292+
}
252293
}
253294
}

Hawaii-Cli/tests/AddEntityTest.cs renamed to Hawaii-Cli/tests/AddEntityTests.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
namespace Hawaii.Cli.Tests
22
{
3-
[TestClass]
4-
public class AddEntityTest
3+
/// <summary>
4+
/// Tests for Adding new Entity.
5+
/// </summary>
6+
[TestClass, TestCategory("AddEntityTests")]
7+
public class AddEntityTests
58
{
69
/// <summary>
710
/// Simple test to add a new entity to json config when there is no existing entity.

Hawaii-Cli/tests/EndToEndTests.cs

Lines changed: 141 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
1-
namespace Hawaii.Cli;
1+
namespace Hawaii.Cli.Tests;
22

3+
/// <summary>
4+
/// End To End Tests for CLI.
5+
/// </summary>
36
[TestClass]
47
public class EndToEndTests
58
{
9+
/// <summary>
10+
/// Initializing config for cosmos DB.
11+
/// </summary>
12+
private static string TEST_RUNTIME_CONFIG = "hawaii-config-test.json";
613
[TestMethod]
714
public void TestInitForCosmosDB()
815
{
9-
string[] args = { "init", "-n", "hawaii-config-test", "--database-type", "cosmos", "--connection-string", "localhost:5000", "--cosmos-database", "graphqldb", "--cosmos-container", "planet", "--graphql-schema", "schema.gql" };
16+
string[] args = { "init", "-n", "hawaii-config-test", "--database-type", "cosmos",
17+
"--connection-string", "localhost:5000", "--cosmos-database",
18+
"graphqldb", "--cosmos-container", "planet", "--graphql-schema", "schema.gql" };
1019
Program.Main(args);
11-
string jsonString = File.ReadAllText("hawaii-config-test.json");
1220

13-
RuntimeConfig? runtimeConfig = JsonSerializer.Deserialize<RuntimeConfig>(jsonString, RuntimeConfig.GetDeserializationOptions());
14-
15-
if (runtimeConfig is null)
16-
{
17-
Assert.Fail("Config was not genertaed correctly.");
18-
}
21+
RuntimeConfig? runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
1922

23+
Assert.IsNotNull(runtimeConfig);
2024
Assert.AreEqual(DatabaseType.cosmos, runtimeConfig.DatabaseType);
2125
Assert.IsNotNull(runtimeConfig.CosmosDb);
2226
Assert.AreEqual("graphqldb", runtimeConfig.CosmosDb.Database);
@@ -31,4 +35,132 @@ public void TestInitForCosmosDB()
3135
Assert.AreEqual(HostModeType.Production, runtimeConfig.HostGlobalSettings.Mode);
3236

3337
}
38+
39+
/// <summary>
40+
/// Test to verify adding a new Entity.
41+
/// </summary>
42+
[TestMethod]
43+
public void TestAddEntity()
44+
{
45+
string[] initArgs = { "init", "-n", "hawaii-config-test", "--database-type", "mssql", "--connection-string", "localhost:5000" };
46+
Program.Main(initArgs);
47+
48+
RuntimeConfig? runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
49+
50+
Assert.IsNotNull(runtimeConfig);
51+
Assert.AreEqual(0, runtimeConfig.Entities.Count()); // No entities
52+
53+
string[] addArgs = {"add", "todo", "-n", "hawaii-config-test", "--source", "s001.todo",
54+
"--rest", "todo", "--graphql", "todo", "--permissions", "anonymous:*"};
55+
Program.Main(addArgs);
56+
57+
runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
58+
Assert.IsNotNull(runtimeConfig);
59+
Assert.AreEqual(1, runtimeConfig.Entities.Count()); // 1 new entity added
60+
Assert.IsTrue(runtimeConfig.Entities.ContainsKey("todo"));
61+
Entity entity = runtimeConfig.Entities["todo"];
62+
Assert.AreEqual("{\"route\":\"/todo\"}", JsonSerializer.Serialize(entity.Rest));
63+
Assert.AreEqual("{\"type\":{\"singular\":\"todo\",\"plural\":\"todos\"}}", JsonSerializer.Serialize(entity.GraphQL));
64+
Assert.AreEqual(1, entity.Permissions.Length);
65+
Assert.AreEqual("anonymous", entity.Permissions[0].Role);
66+
Assert.AreEqual(1, entity.Permissions[0].Actions.Length);
67+
Assert.AreEqual(WILDCARD, GetCRUDOperation((JsonElement)entity.Permissions[0].Actions[0]));
68+
}
69+
70+
/// <summary>
71+
/// Test to verify updating an existing Entity.
72+
/// It tests updating permissions as well as relationship
73+
/// </summary>
74+
[TestMethod]
75+
public void TestUpdateEntity()
76+
{
77+
string[] initArgs = { "init", "-n", "hawaii-config-test", "--database-type",
78+
"mssql", "--connection-string", "localhost:5000" };
79+
Program.Main(initArgs);
80+
81+
RuntimeConfig? runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
82+
83+
Assert.IsNotNull(runtimeConfig);
84+
Assert.AreEqual(0, runtimeConfig.Entities.Count()); // No entities
85+
86+
string[] addArgs = {"add", "todo", "-n", "hawaii-config-test",
87+
"--source", "s001.todo", "--rest", "todo",
88+
"--graphql", "todo", "--permissions", "anonymous:*"};
89+
Program.Main(addArgs);
90+
91+
runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
92+
Assert.IsNotNull(runtimeConfig);
93+
Assert.AreEqual(1, runtimeConfig.Entities.Count()); // 1 new entity added
94+
95+
// Adding another entity
96+
//
97+
string[] addArgs_2 = {"add", "books", "-n", "hawaii-config-test",
98+
"--source", "s001.books", "--rest", "books",
99+
"--graphql", "books", "--permissions", "anonymous:*"};
100+
Program.Main(addArgs_2);
101+
102+
runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
103+
Assert.IsNotNull(runtimeConfig);
104+
Assert.AreEqual(2, runtimeConfig.Entities.Count()); // 1 more entity added
105+
106+
string[] updateArgs = {"update", "todo", "-n", "hawaii-config-test",
107+
"--source", "s001.todos","--graphql", "true",
108+
"--permissions", "anonymous:create,delete",
109+
"--fields.include", "id,content", "--fields.exclude", "rating,level",
110+
"--relationship", "r1", "--cardinality", "one",
111+
"--target.entity", "books", "--mapping.fields", "id:book_id",
112+
"--linking.object", "todo_books",
113+
"--linking.source.fields", "todo_id",
114+
"--linking.target.fields", "id" };
115+
Program.Main(updateArgs);
116+
117+
runtimeConfig = TryGetRuntimeConfig(TEST_RUNTIME_CONFIG);
118+
Assert.IsNotNull(runtimeConfig);
119+
Assert.AreEqual(2, runtimeConfig.Entities.Count()); // No new entity added
120+
121+
Assert.IsTrue(runtimeConfig.Entities.ContainsKey("todo"));
122+
Entity entity = runtimeConfig.Entities["todo"];
123+
Assert.AreEqual("{\"route\":\"/todo\"}", JsonSerializer.Serialize(entity.Rest));
124+
Assert.AreEqual("true", entity.GraphQL!.ToString());
125+
Assert.AreEqual(1, entity.Permissions.Length);
126+
Assert.AreEqual("anonymous", entity.Permissions[0].Role);
127+
Assert.AreEqual(4, entity.Permissions[0].Actions.Length);
128+
//Only create and delete are updated.
129+
Assert.AreEqual("{\"action\":\"create\",\"fields\":{\"include\":[\"id\",\"content\"],\"exclude\":[\"rating\",\"level\"]}}", JsonSerializer.Serialize(entity.Permissions[0].Actions[0]));
130+
Assert.AreEqual("{\"action\":\"delete\",\"fields\":{\"include\":[\"id\",\"content\"],\"exclude\":[\"rating\",\"level\"]}}", JsonSerializer.Serialize(entity.Permissions[0].Actions[1]));
131+
Assert.AreEqual("\"read\"", JsonSerializer.Serialize(entity.Permissions[0].Actions[2]));
132+
Assert.AreEqual("\"update\"", JsonSerializer.Serialize(entity.Permissions[0].Actions[3]));
133+
134+
135+
Assert.IsTrue(entity.Relationships!.ContainsKey("r1"));
136+
Relationship relationship = entity.Relationships["r1"];
137+
Assert.AreEqual(1, entity.Relationships.Count());
138+
Assert.AreEqual(Cardinality.One, relationship.Cardinality);
139+
Assert.AreEqual("books", relationship.TargetEntity);
140+
Assert.AreEqual("todo_books", relationship.LinkingObject);
141+
CollectionAssert.AreEqual(new string[] {"id"}, relationship.SourceFields);
142+
CollectionAssert.AreEqual(new string[] {"book_id"}, relationship.TargetFields);
143+
CollectionAssert.AreEqual(new string[] {"todo_id"}, relationship.LinkingSourceFields);
144+
CollectionAssert.AreEqual(new string[] {"id"}, relationship.LinkingTargetFields);
145+
}
146+
147+
public static RuntimeConfig? TryGetRuntimeConfig(string testRuntimeConfig)
148+
{
149+
string jsonString;
150+
151+
if (!TryReadRuntimeConfig(testRuntimeConfig, out jsonString))
152+
{
153+
return null;
154+
}
155+
156+
RuntimeConfig? runtimeConfig = JsonSerializer.Deserialize<RuntimeConfig>(jsonString, RuntimeConfig.GetDeserializationOptions());
157+
158+
if (runtimeConfig is null)
159+
{
160+
Assert.Fail("Config was not generated correctly.");
161+
}
162+
163+
return runtimeConfig;
164+
}
165+
34166
}

0 commit comments

Comments
 (0)