Webhooks, snapshots and roles with .NET
The Content Management API (CMA) is a restful API for managing content in your Contentful spaces. You can use it to create, update, delete and retrieve content using well-known HTTP verbs.
To make development easier for our users, we publish client libraries for various languages which make the task easier. This article details how to use the .NET client library to create, update and delete webhooks, snapshots and roles.
Pre-requisites
This tutorial assumes you understand the basic Contentful data model as described in the developer center and that you have already read and understand the getting started tutorial for the .NET client library and the using the management API with Contentful and .NET client library article.
Contentful.net is built on .NET Core and targets .NET Standard 2.0. The client library is cross-platform and runs on Linux, macOS, and Windows.
Working with webhooks
To learn more about what webhooks refer to the webhooks concepts article.
To create a webhook for a space use the CreateWebhook
method.
var webhook = new Webhook();
webhook.Name = "New product"; // The name of the webhook
webhook.Url = "https://www.example.com/"; // The url to call when the specified event occurrs
webhook.HttpBasicPassword = "Pass"; // Optional basic auth password
webhook.HttpBasicUsername = "User"; // Optional basic auth username
webhook.Headers = new List<KeyValuePair<string, string>>(); // You can pass custom headers with every call
webhook.Headers.Add(new KeyValuePair<string, string>("custom", "header")); // Add a header by the name of "custom" with a value of "header"
webhook.Topics = new List<string>() // Topics are the events that trigger this webhook.
{
"Entry.*"
};
await client.CreateWebhook(webhook);
The topics available are described in more detail in the webhooks concepts article, but the summary is that you specify a Type
and an Action
. Type
can be any of Entry
, Asset
or ContentType
and Action
can be any of create
, save
, auto_save
, archive
, unarchive
, publish
, unpublish
or delete
. _
is a wildcard and _.\*
is valid
and would mean to call this webhook for all actions across all types.
Once you have created a webhook you can fetch them from the client library using the GetWebhooksCollection
and GetWebhook
methods.
var allWebhooks = await client.GetWebhooksCollection();
var webhook = await client.GetWebhook("<webhook_id>");
To retrieve more information about a specific webhook call use the GetWebhookCallDetailsCollection
to retrieve a list of calls made to the webhook or the GetWebhookCallDetails
to get details of a specific call.
var calldetails = await client.GetWebhookCallDetailsCollection("<webhook_id>");
var calldetail = await client.GetWebhookCallDetails("<call_details_id>", "webhook_id");
A method is available that gives a more general overview of the total number of webhook calls for a specific webhook and whether they returned a success code.
var webhookHealth = await client.GetWebhookHealth("<webhook_id>");
var total = webhookHealth.TotalCalls; // 27
var healthy = webhookHealth.TotalHealthy; // 23
To delete a webhook you are no longer using.
await client.DeleteWebhook("<webhook_id>");
Roles and memberships
The .NET client library allows for the creation of custom roles. It's complex to understand the permissions and policies system, but the examples below should give you a good overview. For more detailed information, refer to the management API documentation.
Creating a role
Start by creating a new Role
and adding the permissions and policies you need.
var role = new Role();
role.Name = "Name of the role";
role.Description = "Describe the role";
role.Permissions = new ContentfulPermissions(); // Add permissions to the role
role.Permissions.ContentDelivery = new List<string>() { "read" }; // What permissions should the role have to the ContentDelivery API
role.Permissions.ContentModel = new List<string>() { "read" }; // What permissions should the role have to the content model, i.e. creating content types and fields
role.Permissions.Settings = new List<string>() { "manage" }; // What permissions should the role have to other type of settings
role.Policies = new List<Policy>(); // Add more granular policies to the role.
role.Policies.Add(new Policy() // Every policy consists of a number of actions and constraints.
{
Effect = "allow",
Actions = new List<string>()
{
"read",
"create",
"update"
},
Constraint = new AndConstraint()
{
new EqualsConstraint()
{
Property = "sys.type",
ValueToEqual = "Entry"
}
}
});
This example above would give the role permissions to read entries, assets, and content types, but not edit, create or delete them. It would also give permissions to manage settings for the space. The policies give this role-specific access to read, create and update entries.
Policies explained
The policies can look daunting and the framework behind them is complex. However, they do make it possible to create granular and flexible authorization rules.
The first property is the Effect
which is how this policy is to be treated, will it allow
or deny
access.
Actions
represents what actions this policy allows or denies. This is a list of strings but only a certain set of values are acceptable. These are read
, create
, update
, delete
, publish
, unpublish
, archive
, unarchive
or all
.
Constraint
is an IConstraint
, which is explained in more detail below.
Constraints explained
Constraints represent which resources a specific policy applies to. There are five different kinds of constraints.
AndConstraint
and OrConstraint
are lists of other IConstraint
. The OrConstraint
ensures that at least one of the contained constraints is true and the AndConstraint
requires all contained constraints to be true.
The NotConstraint
contains another IConstraint
and inverts the value of that constraint.
The EqualsConstraint
contains a Property
which is a field on the content type or asset and a ValueToEqual
which contains the value that this field must equal for this constraint to evaluate to be fulfilled.
The PathConstraint
contains a Fields
property that gives a path that must exist on the content type. An example is "fields.heading"
which would only match content types that have the heading
field present.
Putting them all together looks something like the following.
var constraint = new AndConstraint() // AndConstraint is a list of other IConstraints, ensuring all other contained constraints are met
{
new EqualsConstraint() // This equals constraint constraints this policy to only affect resources with a sys.type of Entry
{
Property = "sys.type",
ValueToEqual = "Entry"
},
new PathConstraint(){ //This path constraint constraints this policy to only affect the name field of the resource
Fields = "fields.name.%"
}
}
Once you've created a Role
and added the appropriate permissions and policies, you can call the CreateRole
method.
var createdRole = await client.CreateRole(role);
You can update, delete and fetch roles with the appropriate methods.
var role = await client.GetRole("<role_id>");
var allRoles = await client.GetAllRoles();
var updatedRole = await client.UpdateRole(role);
await client.DeleteRole("<role_id>");
Working with snapshots
A snapshot of an entry is automatically created each time an entry is published and is the state of every field of the entry at that given time. You can use the snapshots of an entry to keep a full version history of the entry content. For more information read the snapshots documentation.
To get all snapshots for an entry use the GetAllSnapshotsForEntry
. To get a specific snapshot use the SingleSnapshot
method.
var singleSnapshot = await client.SingleSnapshot("<snapshot_id>", "<entry_id>");
var allSnapshots = await client.GetAllSnapshotsForEntry("<entry_id>");
Similarly, a snapshot of a content type is created each time a content type is published and can be fetched using the GetAllSnapshotsForContentType
and GetSnapshotForContentType
methods.
var singleSnapshot = await client.GetSnapshotForContentType("<snapshot_id>", "<content_type_id>");
var allSnapshots = await client.GetAllSnapshotsForContentType("<content_type_id>");
Working with space memberships
A space membership represents the membership type a user has in a space and you can assign additional roles to a user, flag a user as admin or remove a user from a space.
To get all memberships of a space call the GetSpaceMemberships
method. To get a single membership call GetSpaceMembership
method.
var singleMembership = await client.GetSpaceMembership("<membership_id>");
var roles = singleMembership.Roles; // A List<string> of all role ids this membership has
var userId = singleMembership.User.SystemProperties.Id; // The id of the user tied to this membership
var isAdmin = singleMembership.Admin; // Whether this membership is the administrator of the space
var allMemberships = await client.GetSpaceMemberships();
To update an existing membership use the UpdateSpaceMembership
method.
var singleMembership = await client.GetSpaceMembership("<membership_id>");
singleMembership.Admin = false; // This membership will no longer be administrator of the space
await client.UpdateSpaceMembership(singleMembership);
To create a new membership use the CreateSpaceMembership
method.
var newMembership = new SpaceMembership();
newMembership.Roles = new List<string>() {
"<role_id>"
};
newMembership.Admin = true;
newMembership.User = new User() { // The user must already exist. It cannot be automatically created through the membership.
SystemProperties = new SystemProperties() {
Id = "<user_id>",
LinkType = "User",
Type = "Link"
}
};
await client.CreateSpaceMembership(newMembership);
To delete a membership use the DeleteSpaceMembership
method.
await client.DeleteSpaceMembership("<membership_id>");
Working with API keys
The Contentful .NET client library allows you to get all API keys for the Content Delivery API ((not the management API) for a space. It also allows you to create new API keys.
var allApiKeys = await client.GetAllApiKeys();
var accessToken = allApiKeys.First().AccessToken;
var newKey = await client.CreateApiKey("Name of key", "Description of key");
var newAccessToken = newKey.AccessToken;
Working with environments
To create an environment call the CreateOrUpdateEnvironment
method.
var environment = await client.CreateOrUpdateEnvironment("<environment_id>", "<environment_name>");
This environment will be an identical copy to your current master environment. Note that it can take a while for the environment to be fully available depending on the size of the master environment.
To get a list of available environments use the GetEnvironments
method.
var environments = await client.GetEnvironments();
To get a specific environment use the GetEnvironment
method.
var environment = await client.GetEnvironment("<environment_id>");
To delete an environment use the DeleteEnvironment
method.
await client.DeleteEnvironment("<environment_id>");
Next steps
Not what you’re looking for? Try our FAQ.