Skip to content

Commit 7286ccd

Browse files
Improvements to the ogschema tool (#36)
* Improvements to the ogschema tool Added more documentation Added file and directory validation Added support for custom file name for output file Added support for specifying your own namespace Made all classes partial. This allow developers to add helper methods etc. without changing the generated file. Made parameter parsing more robust Uses IO classes to handle OS specific path delimiters * Bugfix: check if schema property has searchable attribute Custom content source might not have this attribute, and the generation fails. Added check, returns false by default. * Missed the partial on classes with inheritance --------- Co-authored-by: manh.nguyen <[email protected]>
1 parent e9db0de commit 7286ccd

File tree

2 files changed

+125
-27
lines changed

2 files changed

+125
-27
lines changed

APIs/src/CGTypeSync/Program.cs

+76-20
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,39 @@ public class Program
99
private static string USER_AGENT => $"Optimizely-Graph-Tools/{typeof(Program).Assembly.GetName().Version}";
1010
static void Main(string[] args)
1111
{
12-
Console.WriteLine("************************************ Optimizely Content Graph Client Tools ************************************");
13-
Console.WriteLine("This tool will generate C# classes from Optimizely Graph schema. Ensure you config correct account");
14-
15-
string configPath = "appsettings.json", directory = ".", source = "default";
12+
Console.WriteLine("**** Optimizely Content Graph Client Tools ****");
13+
Console.WriteLine("Version: " + USER_AGENT);
14+
Console.WriteLine("This tool will generate C# classes from Optimizely Graph schema.");
15+
Console.WriteLine("Learn more: https://github.com/episerver/graph-net-sdk");
16+
Console.WriteLine("");
17+
Console.WriteLine("Usage:");
18+
Console.WriteLine("dotnet ogschema <settingsfile> <output> <optional-source> <optional-namespace>");
19+
Console.WriteLine("settingsfile: Relative or full path to an appSettings.json file with Graph configuration");
20+
Console.WriteLine("output: Relative or full path to a directory or .cs file to write generated C# models to");
21+
Console.WriteLine("optional-source: The content source in your Graph instance, default value is: \"default\"");
22+
Console.WriteLine("optional-namespace: The namespace for the generated C# classes");
23+
24+
25+
const string defaultFileName = "GraphModels.cs";
26+
string configPath = "appsettings.json",
27+
output = ".",
28+
source = "default",
29+
modelNamespace = "Optimizely.ContentGraph.DataModels";
1630
if (args.Length > 0)
1731
{
1832
configPath = args[0];
1933
}
34+
35+
FileInfo fi = new FileInfo(configPath);
36+
if (fi.Exists == false)
37+
{
38+
Console.WriteLine("Cannot locate settings file: {0}", fi.FullName);
39+
Environment.ExitCode = -1;
40+
return;
41+
}
42+
43+
// Make sure we operate on a full path
44+
configPath = fi.FullName;
2045

2146
IConfigurationRoot config = new ConfigurationBuilder()
2247
.AddJsonFile(configPath)
@@ -27,26 +52,58 @@ static void Main(string[] args)
2752
{
2853
if (args.Length > 1)
2954
{
30-
directory = args[1];
55+
// First, check if output is a file (we don't care if it exists or not)
56+
fi = new FileInfo(args[1]);
57+
if (string.IsNullOrEmpty(fi.Extension) == false)
58+
{
59+
// we have a file, just pass that on
60+
output = fi.FullName;
61+
}
62+
else
63+
{
64+
// Not a file, assume it is a directory
65+
var di = new DirectoryInfo(args[1]);
66+
output = Path.Combine(di.FullName, defaultFileName);
67+
}
3168
}
3269

3370
if (args.Length > 2)
3471
{
3572
source = args[2];
3673
}
3774

38-
Run(config, directory, source);
75+
if (args.Length > 3)
76+
{
77+
modelNamespace = args[3];
78+
}
79+
80+
Console.WriteLine("");
81+
Console.WriteLine("Running with the following settings");
82+
Console.WriteLine(" Configuration file: {0}", configPath);
83+
Console.WriteLine(" Output: {0}", output);
84+
Console.WriteLine(" Content Source: {0}", source);
85+
Console.WriteLine(" Namespace: {0}", modelNamespace);
86+
Console.WriteLine("");
87+
Run(config, output, source, modelNamespace);
88+
}
89+
else
90+
{
91+
Console.WriteLine("Cannot build settings from settings file: {0}", configPath);
92+
Environment.ExitCode = -1;
3993
}
4094
}
41-
public static void Run(IConfiguration configuration, string output, string source)
95+
public static void Run(IConfiguration configuration,
96+
string output,
97+
string source,
98+
string modelNamespace)
4299
{
43100
//parse the CGTypes.json file
44101
var schemaTypes = new Dictionary<string, List<Tuple<string, string>>>();
45-
const string fileName = "GraphModels.cs";
46102
JObject json = GetSchemaDataTypes(configuration, source);
47103
if (json == null)
48104
{
49105
Console.WriteLine("Error! schema is null");
106+
Environment.ExitCode = -1;
50107
return;
51108
}
52109
var propertyTypes = json["propertyTypes"];
@@ -63,14 +120,14 @@ public static void Run(IConfiguration configuration, string output, string sourc
63120
sb.AppendLine("using System.Globalization;");
64121
sb.AppendLine();
65122
sb.AppendLine($"#pragma warning disable CS0108, CS0114");
66-
sb.AppendLine("namespace Optimizely.ContentGraph.DataModels");
123+
sb.AppendLine("namespace " + modelNamespace);
67124
sb.AppendLine("{");
68125

69126
foreach (JProperty propertyType in propertyTypes)
70127
{
71128
var propertyTypeName = propertyType.Name;
72129

73-
sb.AppendLine($" public class {propertyTypeName}");
130+
sb.AppendLine($" public partial class {propertyTypeName}");
74131
sb.AppendLine (" {");
75132

76133
var properties = propertyType.First().Children().Children().Children();
@@ -99,15 +156,15 @@ public static void Run(IConfiguration configuration, string output, string sourc
99156
inheritedFromType = string.Join(',', parentTypes.Select(type=> type.ToString()));
100157
inheritedFromType = $":{inheritedFromType}";
101158
}
102-
sb.AppendLine($" public class {propertyTypeName}{inheritedFromType}");
159+
sb.AppendLine($" public partial class {propertyTypeName}{inheritedFromType}");
103160
sb.AppendLine(" {");
104161

105162
var properties = contentType.First().Children().Children().Children();
106163

107164
foreach (JProperty propertyProperty in properties.Where(x => x is JProperty))
108165
{
109166
var name = propertyProperty.Name;
110-
var searchableAttr = (bool)(propertyProperty.Children()["searchable"].First() as JValue).Value;
167+
var searchableAttr = (bool)((propertyProperty.Children()["searchable"].FirstOrDefault() as JValue)?.Value ?? false);
111168
var dataType = (propertyProperty.Children()["type"].First() as JValue).Value.ToString();
112169
dataType = ConvertType(dataType);
113170
if (searchableAttr)
@@ -125,19 +182,18 @@ public static void Run(IConfiguration configuration, string output, string sourc
125182

126183
sb.AppendLine("}");
127184
var classes = sb.ToString();
128-
if (string.IsNullOrEmpty(output))
185+
FileInfo fi = new FileInfo(output);
186+
// Check if directory exists, create if not
187+
if(fi.Directory != null && fi.Directory.Exists == false)
129188
{
130-
output = Directory.GetCurrentDirectory();
189+
fi.Directory.Create();
131190
}
132-
if (!Directory.Exists(output)){
133-
Directory.CreateDirectory(output);
134-
}
135-
var outFile = Path.Combine(output, fileName);
136-
using (var writer = new StreamWriter(outFile, false))
191+
192+
using (var writer = new StreamWriter(output, false))
137193
{
138194
writer.Write(sb.ToString());
139195
}
140-
Console.WriteLine($"Classes had been generated to {output}/{fileName}.");
196+
Console.WriteLine($"Optimizely Graph model C# classes have been written to {output}.");
141197
}
142198

143199
private static string ConvertType(string propType)

README.md

+49-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ From repository level:
1717

1818
#### Run Integration tests
1919
- Configure appsettings.json file in `.\APIs\src\Testing\EPiServer.ContentGraph.IntegrationTests` to connect to GraphQL gateway.
20-
If appsettings.json was ready, from repository level:
20+
If appsettings.json was ready, from repository level:
2121
- Run `cd msbuild`
2222
- Run `powershell .\intergrationTest.ps1` if you're using Command Line or `.\intergrationTest.ps1` if PowerShell
2323

@@ -32,15 +32,57 @@ If appsettings.json was ready, from repository level:
3232
- On the way...
3333

3434
### Optimizely Graph Client Tool
35-
This tool supports for generating Optimizely Graph's schema to C# object models. You do not need to create models manually if install this tool.
35+
This tool will generate C# classes (models) based on Optimizely Graph's schema. The Client SDK graph builder classes uses these models to provide strongly typed access to the Graph data.
36+
3637
#### Install Optimizely Graph Client Tool
37-
From repository level:
38-
- Run `mkdir ogtools`
39-
- Run `cd ogtools`
38+
You typically want to install this tool into your solution directory. It is installed as a `dotnet tool` which you can invoke using the `dotnet` command. If you install the tool at the root of your solution (where your .sln and/or .git directory is located), the command will also work in all subdirectories.
39+
Open a command line, navigate to the root of your repository/solution:
4040
- Run `dotnet new tool-manifest`
4141
- Run `dotnet tool install Optimizely.Graph.Client.Tools --local`
42-
- Run `dotnet ogschema path_to_your_appsettings.json path_to_store_your_models`
43-
When the last command is succeed you will see the models will be generated in file `GraphModels.cs` located in the `path_to_store_your_models` you have ran in last command.
42+
43+
You can now invoke the `ogschema` tool using the follwing command:\
44+
`dotnet ogschema path_to_your_appsettings.json path_to_store_your_models`
45+
46+
This will create a file `GraphModels.cs` in the `path_to_store_your_models` directory.
47+
48+
#### Example installation
49+
50+
You have an ASP.NET project in `c:\dev\alloy\`, and the following file setup
51+
```
52+
alloy.sln
53+
\src
54+
alloy.csproj
55+
\Models
56+
\Controllers
57+
...
58+
\docs
59+
readme.md
60+
```
61+
1. Open a command prompt at the root of your ASP.NET project (`c:\dev\alloy\`)
62+
2. Run the following command: `dotnet new tool-manifest`
63+
3. This will generate a `.config` folder with a dotnet tool manifest
64+
4. Run the following command: `dotnet tool install Optimizely.Graph.Client.Tools --local`
65+
5. This will register the ogschema tool in the manifest, and allow the tool to be invoked anywhere in or below `c:\dev\alloy\`
66+
6. Change to your src directory: `cd src`
67+
7. Run the following command: `dotnet ogschema appsettings.json Models`
68+
8. This will generate a file called `GraphModels.cs` in the Models directory
69+
70+
In this example, your `appSettings.config` file must have the necessary Optimizely Graph settings. See the [developer documentation](https://docs.developers.optimizely.com/platform-optimizely/v1.4.0-optimizely-graph/docs/configure-package-settings-in-aspnet-core) for more information.
71+
72+
#### Command Line Reference
73+
`dotnet ogschema <settingsfile> <output> <optional-source> <optional-namespace>`
74+
75+
* `settingsfile`: Relative or full path to an appSettings.json file with Graph configuration
76+
* `output`: Relative or full path to a directory or .cs file to write generated C# models to
77+
* `optional-source`: The content source in your Graph instance, default value is: *default*
78+
* `optional-namespace`: The namespace for the generated C# classes
79+
80+
The following command:\
81+
`dotnet ogschema appsettings.json Models\MyContentSource.cs dt1 Alloy.Models`
82+
1. will look for an `appsettings.json` file in the current directory
83+
2. will create a file called `MyContentSource.cs` in the `Models` directory below the current directory
84+
3. will generate model classes for the [content source](https://docs.developers.optimizely.com/platform-optimizely/v1.4.0-optimizely-graph/docs/synchronize-content-types) called `dt1`. This could be a custom content source that you have created previously
85+
4. The namespace used in the `MyContentSource.cs` file will be `Alloy.Models`
4486

4587
### Contributing
4688

0 commit comments

Comments
 (0)