C# Cookbook
Getting Started
To get started with the C# driver, visit Gitana C# Driver Page or the Github Page. It is written with .NET Core and can be used in any compatible project.
You can install the driver via the command line:
dotnet add package cloudcms
or from within Visual Studio:
Install-Package cloudcms
Or by adding this to your .csproj
file (you may have to adjust the version):
<ItemGroup>
<PackageReference Include="cloudcms" Version="1.1.1" />
</ItemGroup>
Connecting to Gitana
There are several ways to connect the driver to Gitana.
Using a Gitana JSON file
You can use a gitana.json
file included in your build. It should looks something like:
{
"clientKey": "{your client key}",
"clientSecret": "{your client secret}",
"username": "{your username or auth grant key}",
"password": "{your password or auth grant secret}",
"baseURL": "{the url to the Gitana API endpoint}"
}
You will need to specify the path into upon connecting:
string path = "gitana.json";
IPlatform platform = await CloudCMSDriver.ConnectAsync(path);
Specify Values Directly
You can also connect to using a Dictionary, JObject, or ConnectionConfig
object.
Dictionary:
IDictionary<string, string> configDict = new Dictionary<string, string>();
configDict.Add("clientKey", "{your client key}");
configDict.Add("clientSecret", "{your client secret}");
configDict.Add("username", "{your username or auth grant key}");
configDict.Add("password", "{your password or auth grant secret}");
configDict.Add("baseURL", "{the url to the Cloud CMS API endpoint}");
IPlatform platform = await CloudCMSDriver.ConnectAsync(configDict);
JObject:
JObject configObj = new JObject(
new JProperty("clientKey", "{your client key}");
new JProperty("clientSecret", "{your client secret}");
new JProperty("username", "{your username or auth grant key}");
new JProperty("password", "{your password or auth grant secret}");
new JProperty("baseURL", "{the url to the Cloud CMS API endpoint}");
);
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
ConnectionConfig:
ConnectionConfig config = new ConnectionConfig();
config.clientKey = "{your client key}");
config.clientSecret = "{your client secret}");
config.username = "{your username or auth grant key}");
config.password = "{your password or auth grant secret}");
config.baseURL = "{the url to the Cloud CMS API endpoint}");
IPlatform platform = await CloudCMSDriver.ConnectAsync(config);
Code Samples
Here are some code samples to help you get started. In these scenarios, we assume you're using the most simple connection method (providing the path to gitana.json
).
In Cloud CMS, you can have as many repositories as you'd like. And each repository can multiple branches (similar to Git). The first thing you should do is connect and get the branch that you want to work on.
For the purpose of most of these examples, we'll assume the repository ID is 1234567890abcdef1234
and the branch is master
.
Get your Repository
Use the ReadRepositoryAsync
method to fetch a specific repository. The user you've logged in as must have READ
permissions to the repository. If they do not have read rights, a null response is expected. Also ensure that you await
the results and call from within an async
method.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
Get the Master Branch
Use the ReadBranchAsync
method to fetch a specific branch. The user you've logged in as must have READ
permissions to the branch. If they do not have read rights, a null response is expected.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
Node Creation
Create a Node
{
"title": "Custom Article",
"_qname": "custom:article",
"type": "object",
"properties": {
"title": {
"type": "string"
},
"body": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"type": "string"
}
},
"rating": {
"type": "number"
}
}
}
Create a content instance of this type like this:
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject obj = new JObject();
obj["_type"] = "custom:article";
obj["title"] = "Article #1";
obj["body"] = "A still more glorious dawn awaits";
obj["categories"] = JArray.FromObject(new[] { "category1", "category2", "category3" });
obj["rating"] = 5
INode node = (INode) await branch.CreateNodeAsync(obj);
You'll notice that we cast the result here to INode
. The CreateNodeAsync
method returns IBaseNode
, which can represent either a Node or an Association, so the cast here gives us access to methods only available to proper nodes.
Create a Folder
In Cloud CMS, folders are just nodes with the f:container
feature on them.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject obj = new JObject();
obj["title"] = "My Folder";
INode folder = (INode) await branch.CreateNodeAsync(obj);
await folder.AddFeatureAsync("f:filename", new JObject());
await folder.AddFeatureAsync("f:container", new JObject());
// The root node of the branch
INode root = branch.RootNodeAsync();
// Associate our new node to the root node via an a:child relationship
await root.AssociateAsync(folder, QName.create("a:child"), Directionality.DIRECTED);
An easier way to do this is to use the following special properties when the node is created:
_parentFolderPath
- the path to the parent folder where the created node should be linked. The folder must exist. If it does not exist, the operation will fail._fileName
- thef:filename
to be assigned to the node once it is created.
Alternatively, you can use:
_filePath
- an absolute path where the file should be created. This allows you to specify the parent folder path and the file name at the same time,.
You can also adjust the association type (between the discovered parent folder and your newly created item).
_associationTypeString
- the QName for the newly created association. The default isa:child
.
Thus, you could do:
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject obj = new JObject();
obj["title"] = "My Folder";
obj["_parentFolderPath"] = "/";
obj["_fileName"] = "my_folder";
INode folder = (INode) await branch.CreateNodeAsync(obj);
Or:
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject obj = new JObject();
obj["title"] = "My Folder";
obj["_filePath"] = "/my_folder";
INode folder = (INode) await branch.CreateNodeAsync(obj);
Associations
In Gitana, any two nodes can be connected via an association. You can use an out-of-the-box association type, such as a:linked
or a:owned
or you can create your own.
Let's imagine that we have two articles that look like this:
- Article #1
{
"_doc": "1234567890abcdef1111",
"_type": "custom:article",
"title": "Article #1",
"body": "a mote of dust suspended in a sunbeam",
"categories": ["category1", "category2"],
"rating": 1
}
- Article #2
{
"_doc": "1234567890abcdef2222",
"_doc": "",
"_type": "custom:article",
"title": "Article #2",
"body": "harvesting star light",
"categories": ["category2", "category3"],
"rating": 2
}
Let's create an association that points from the first article (1234567890abcdef1111
) to the second article (1234567890abcdef2222
), like this:
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
INode article1 = (INode) await branch.ReadNodeAsync("1234567890abcdef1111");
INode article2 = (INode) await branch.ReadNodeAsync("1234567890abcdef2222");
QName associationTypeQName = QName.create("a:linked");
IAssociation association = await article1.AssociateAsync(article2, associationTypeQName);
You can also find all associations around article1
. This will include associations that are INCOMING
, OUTGOING
and MUTUAL
.
List<IAssociation> associations = await article1.AssociationsAsync();
Or find only the INCOMING
associations:
List<IAssociation> associations = await article1.AssociationsAsync(Direction.INCOMING);
Or find only the OUTGOING
associations of type a:linked
:
List<IAssociation> associations = await article1.AssociationsAsync(associationTypeQName, Direction.INCOMING);
Query
Basic Querying
Find all of the nodes that have a category
with the value category1
.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject query = new JObject();
query["_type"] = "custom:article";
query["category"] = "category1";
List<IBaseNode> results = await branch.QueryNodesAsync(query);
Console.WriteLine("The size was: " + results.Count) // 3
foreach (IBaseNode n in results)
{
INode node = (INode) n;
Console.WriteLine("Found a result, title: " + node.Data["title"] + ", rating: " + node.Data["rating"]);
}
Pagination
If you have a lot of potential query hits, you'll want to paginate. Here is an example where we return results starting at index 2 and hand back at most 5 results.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject query = new JObject();
query["_type"] = "custom:article";
query["category"] = "category1";
JObject pagination = new JObject();
pagination["skip"] = 2;
pagination["limit"] = 5;
List<IBaseNode> results = await branch.QueryNodesAsync(query, pagination);
Console.WriteLine("The size was: " + results.Count) // 3
foreach (IBaseNode n in results)
{
INode node = (INode) n;
Console.WriteLine("Found a result, title: " + node.Data["title"] + ", rating: " + node.Data["rating"]);
}
Sorting
Use the pagination option to sort the results as you see fit. Here we sort descending on rating
.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject query = new JObject();
query["_type"] = "custom:article";
query["category"] = "category1";
JObject pagination = new JObject();
pagination["sort"] = new JObject();
pagination["sort"]["rating"] = -1;
List<IBaseNode> results = await branch.QueryNodesAsync(query, pagination);
Console.WriteLine("The size was: " + results.Count) // 3
foreach (IBaseNode n in results)
{
INode node = (INode) n;
Console.WriteLine("Found a result, title: " + node.Data["title"] + ", rating: " + node.Data["rating"]);
}
The results will look like:
Found a result, title: Article #3, rating: 3
Found a result, title: Article #2, rating: 2
Found a result, title: Article #1, rating: 1
Query for values in a range
Find all of the articles where the rating is greater than or equal to 2.
This demonstrates the power of MongoDB's query language.
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
JObject query = new JObject();
query["_type"] = "custom:article";
query["rating"] = new JObject();
query["rating"]["$gte"] = 2;
List<IBaseNode> results = await branch.QueryNodesAsync(query);
Search for Nodes
Let's now look at searching for content. This makes use of Elastic Search's DSL to express some very powerful full text and structured searches. We recommend further reading on Search within Gitana.
We also suggest reading up on Query Strings to understand how you can write searches textually (in addition to structuring them as JSON objects):
Let's assume that we have the following:
- Article #1
{
"_type": "custom:article",
"title": "Article #1",
"body": "a mote of dust suspended in a sunbeam",
"categories": ["category1", "category2"],
"rating": 1
}
- Article #2
{
"_type": "custom:article",
"title": "Article #2",
"body": "harvesting star light",
"categories": ["category2", "category3"],
"rating": 2
}
- Article 3
{
"_type": "custom:article",
"title": "Article #3",
"body": "we are star stuff",
"categories": ["category3", "category1"],
"rating": 3
}
Full Text Search
Find all of the nodes with the word star
. Simple enough!
IPlatform platform = await CloudCMSDriver.ConnectAsync(configObj);
IRepository repository = await platform.ReadRepositoryAsync("1234567890abcdef1234");
if (repository == null)
{
throw new SystemException("Unable to read repository: 1234567890abcdef1234");
}
IBranch branch = await repository.ReadBranchAsync("master");
if (branch == null)
{
throw new SystemException("Unable to read master branch");
}
List<IBaseNode> results = await branch.SearchNodesAsync("star");
The results will be:
Found a result, title: Article #2, body: harvesting star light
Found a result, title: Article #3, body: we are star stuff
GraphQL
You can make queries for specific fields or content in related items by using GraphQL. Here we do a simple query for just the title and body of our article type:
string query = @"query {
n_nodes {
title
}
}";
JObject results = await branch.GraphqlQuery(query);