Custom REST Scopes
If you have created custom REST endpoints then you should also be creating custom scopes for those endpoints (see What are REST API Scopes? for information about REST scopes and how they're used in Verint Community).
To create scopes you must first that implements the IRestScopeGroups plugin interface.
This can be a standalone plugin however since this needs to be enabled along with any custom REST endpoints, it's often combined with the IRestEndpoints plugin implementation that will use it . At minimum these two plugins should be part of a IPluginGroup so they are enabled together as they are dependent.
When creating custom scopes, you are really just defining a scope group and its entities. The individual scopes are generated using the types of REST endpoint routes associated with them . It is important before trying to create custom scopes you first read and understand what REST scopes are and how they are defined .
Here is an example of an IRestEndpoints implementation that defines a custom scope group and entity by extending IRestEndpoints with IRestScopeGroups:
using System.Collections.Generic;
using Telligent.Evolution.Extensibility.Version1;
using Telligent.Evolution.Extensibility.Rest.Infrastructure.Version1;
using Telligent.Evolution.Extensibility.Rest.Version2;
public class CustomRestEndpointsWithScopeGroup : ITranslatablePlugin, IRestScopeGroups, IRestEndpoints
{
private static string _restScopeGroupId => "mygroup";
private static string _restScopeEntityId => "myentity";
#region IPlugin
public string Name => "Custom Rest Scopes";
public string Description => "Defines the available REST scopes for Custom REST APIs";
public void Initialize()
{
}
#endregion
#region IRestEndpoints
public void Register(IRestEndpointController restRoutes)
{
var getEndpointDocumentation = new RestEndpointDocumentation()
{
EndpointDocumentation = new EndpointDocumentation
{
Resource = "Samples",
Action = "Get"
}
};
getEndpointDocumentation.RequestDocumentation.Add(new RequestDocumentation
{
Name = "id",
Type = typeof(int),
Description = "Id of the entity to return.",
Required = RestRequired.Required
});
restRoutes.Add(2, "sampleendpoint/sampleentity/{id}", Telligent.Evolution.Extensibility.Rest.Version2.HttpMethod.Get, (request, response) =>
{
// TODO: Implement API
response.StatusCode = 200;
},
new RestRouteCreateOptions()
{
Documentation = getEndpointDocumentation,
ScopeEntityAssignments = new List<RestScopeEntityAssignment>()
{
new RestScopeEntityAssignment(_restScopeGroupId, _restScopeEntityId)
}
}
);
var postEndpointDocumentation = new RestEndpointDocumentation()
{
EndpointDocumentation = new EndpointDocumentation
{
Resource = "Samples",
Action = "Get"
}
};
// TODO: Additional parameters should be added to postEndpointDocumetnation
restRoutes.Add(2, "sampleendpoint/sampleentity", Telligent.Evolution.Extensibility.Rest.Version2.HttpMethod.Post, (request, response) =>
{
// TODO: Implement API
response.StatusCode = 201;
},
new RestRouteCreateOptions()
{
Documentation = postEndpointDocumentation,
ScopeEntityAssignments = new List<RestScopeEntityAssignment>()
{
new RestScopeEntityAssignment(_restScopeGroupId, _restScopeEntityId)
}
}
);
}
#endregion
#region ITranslatablePlugin
private ITranslatablePluginController _translations;
public void SetController(ITranslatablePluginController controller)
{
_translations = controller;
}
public Translation[] DefaultTranslations
{
get
{
var enUS = new Translation("en-us");
enUS.Set("Custom_ScopeGroup_Name", "My Custom Scope");
enUS.Set("Custom_ScopeGroup_Description", "A custom scope group.");
enUS.Set("Custom_Entity_Name", "myentity");
return new[] { enUS };
}
}
#endregion
#region IRestScopeGroups
public void RegisterScopeGroups(IRestScopeGroupRegistrationController controller)
{
var newScopeEntities = new List<RestScopeEntity>();
newScopeEntities.Add(new RestScopeEntity(_restScopeEntityId, () => _translations.GetLanguageResourceValue("Custom_Entity_Name")));
controller.Add(
_restScopeGroupId,
() => _translations.GetLanguageResourceValue("Custom_ScopeGroup_Name"),
() => _translations.GetLanguageResourceValue("Custom_ScopeGroup_Description"),
newScopeEntities
);
}
#endregion
}
You will also see we implemented ITranslatablePlugin which allows for static strings to be translated . This is not required, however because the group and entity names are displayed in the user interface, this is the recommended best practice.
Breaking It Down
First we define instance level static or read-only string values that are the scope group id and the entity id we are defining. These values never change and we will use them later. If your REST endpoints are part of a separate plugin you may consider making these public for use later.
private static string _restScopeGroupId => "mygroup"; private static string _restScopeEntityId => "myentity";
In RegisterScopeGroups() we actually define the new scope group and its entity.
public void RegisterScopeGroups(IRestScopeGroupRegistrationController controller)
{
var newScopeEntities = new List<RestScopeEntity>();
newScopeEntities.Add(new RestScopeEntity(_restScopeEntityId, () => _translations.GetLanguageResourceValue("Custom_Entity_Name")));
controller.Add(
_restScopeGroupId,
() => _translations.GetLanguageResourceValue("Custom_ScopeGroup_Name"),
() => _translations.GetLanguageResourceValue("Custom_ScopeGroup_Description"),
newScopeEntities
);
}
Even though it’s not illustrated here you can register multiple scope groups in a single plugin by simply calling IRestGroupRegistrationController.Add(). The same also applies to entities. A scope group can and usually does contain multiple entities. In the example we instantiate a list of RestScopeEntity, add the entity or entities we want to assign to the group, and pass that in to IRestGroupRegistrationController.Add() along with the scope group id, name and description.
One thing to notice is that the name and description options on groups and entities are not string properties but rather Func<string>(). Scopes in general are long lived and static in terms of their lifespan,and in order to facilitate translation as we do in this plugin, the values for these properties are required to be more dynamic so they can be loaded on access. In this case because the Func<string>() calls our translation controller, the string returned is in the appropriate language for the accessing user, if defined.
Lastly when defining an endpoint or endpoints, we need to assign that endpoint to our scope group and entity which is done by assigning RestRouteCreateOptions.ScopeEntityAssignments to a list of ScopeEntityAssignment objects. Note that an endpoint can belong to multiple scope groups and entities, though it's not common.
public void Register(IRestEndpointController restRoutes)
{
var getEndpointDocumentation = new RestEndpointDocumentation()
{
EndpointDocumentation = new EndpointDocumentation
{
Resource = "Samples",
Action = "Get"
}
};
getEndpointDocumentation.RequestDocumentation.Add(new RequestDocumentation
{
Name = "id",
Type = typeof(int),
Description = "Id of the entity to return.",
Required = RestRequired.Required
});
restRoutes.Add(2, "sampleendpoint/sampleentity/{id}", Telligent.Evolution.Extensibility.Rest.Version2.HttpMethod.Get, (request, response) =>
{
// TODO: Implement API
response.StatusCode = 200;
},
new RestRouteCreateOptions()
{
Documentation = getEndpointDocumentation,
ScopeEntityAssignments = new List<RestScopeEntityAssignment>()
{
new RestScopeEntityAssignment(_restScopeGroupId, _restScopeEntityId)
}
}
);
var postEndpointDocumentation = new RestEndpointDocumentation()
{
EndpointDocumentation = new EndpointDocumentation
{
Resource = "Samples",
Action = "Get"
}
};
// TODO: Additional parameters should be added to postEndpointDocumetnation
restRoutes.Add(2, "sampleendpoint/sampleentity", Telligent.Evolution.Extensibility.Rest.Version2.HttpMethod.Post, (request, response) =>
{
// TODO: Implement API
response.StatusCode = 201;
},
new RestRouteCreateOptions()
{
Documentation = postEndpointDocumentation,
ScopeEntityAssignments = new List<RestScopeEntityAssignment>()
{
new RestScopeEntityAssignment(_restScopeGroupId, _restScopeEntityId)
}
}
);
}If you recall from the article on Rest Scopes in general, an actual scope is a 3 part string that is:
groupId.entityId.accessModifier
The access modifier is determined by the HTTP method, so endpoints that use GET are readonly access, while POST/PUT/DELETE fall under modify. So given we have 2 endpoints, one GET and one POST, both in the same scope and entity group, we will have 2 new scopes available:
mygroup.myentity.readonly
mygroup.myentity.modify
These are the actual strings used when asking for scopes via OAuth. Once the and enabled, you will see these options are now available for Api Keys and for OAuth clients to configure.