Developer Guide
Alpaca provides a large set of prebuilt tools and processes to perform tasks and provide automation for BroadWorks platforms. When the built-in tools do not meet all use-case needs Alpaca can be extended and used as a library to meet those needs. As a Java library, it provides access to all the BroadWorks OCI requests in an easy-to-use fashion. Alpaca also contains framework code that bundles requests for increased throughput, handles automatic reconnection, and aids in error management.
Rest API
The Alpaca service includes a Rest API. Full endpoint documentation can be found here.
Authenticating
There are two options for authenticating scripts against the Alpaca Rest API - username/password or token.
Username / Password Authentication
When using username/password authentication, the username and password are required with each restful call.
cUrl Example- Retrieve list of Clusters
-
curl -X GET https://{{username}}:{{password}}@{{alpaca_url}}:{{port}}/api/v1/clusters/
- username: The Alpaca User's username
- password: The Alpaca User's password
- alpaca_url: The URL for the Alpaca server.
- port: The port for the Alpaca server, if configured.
-
Token Authentication
When using token authentication, once a token is generated, it can be passed with each subsequent request without the need to re-login.
- Steps:
- Post to the login endpoint -
/api/login
- passingusername
andpassword
as a body object. - If credentials are valid, three headers will be returned
SESSION
XSRF-TOKEN
X-Alpaca-Id
- These headers will need to be passed along for subsequent requests.
- Post to the login endpoint -
-
Retrieve list of Clusters
- Login
-
curl -v -X POST https://{{alpaca_url}}:{{port}}/api/login -F username={{username}} -F password={{password}}
- username: The Alpaca User's username
- password: The Alpaca User's password
- alpaca_url: The URL for the Alpaca server.
- port: The port for the Alpaca server, if configured.-
- Response example:
... < HTTP/1.1 200 OK ... < Set-Cookie: XSRF-TOKEN=c1b838b8-48ed-4999-9a9f-5ca4220077ba; Path=/ < Set-Cookie: X-Alpaca-Id=server-R22-11d5ebc3-b40e-417b-b8d2-4da139d2ebe8; Path=/; Max-Age=120; Expires=Tue, 22 Nov 2022 19:07:28 GMT; Secure; HttpOnly; SameSite=Strict < Set-Cookie: SESSION=ZmMyODliZDUtZZM4ZS00Mzc3LThjYzktNDRjNDMyYmMxNTZk; Path=/; HttpOnly; SameSite=Lax ...
- Clusters endpoint
curl -X GET -H "XSRF-TOKEN: c1b838b8-48ed-4999-9a9f-5ca4220077ba" -H "SESSION=ZmMyODliZDUtZZM4ZS00Mzc3LThjYzktNDRjNDMyYmMxNTZk" https://{{alpaca_url}}:{{port}}/api/v1/clusters/
-
- Login
Alpaca Headers
-
x-broadworks
: The version of BroadWorks to use for the request. This is required when the endpoint is nested under/api/cluster
. Valid values: R22, R23, or R24. -
x-alpaca-id
: The Alpaca instance to use. Only needed if you wish to hit a specific Alpaca instance, otherwise your request will be load balanced. This header is returned for each response sent to Alpaca.
Other Headers
-
Content-Type
- This header is required when performing a PUT or POST with a body. The supported content type isapplication/json
. - Example:curl -X POST --header "Authorization: Basic YWRtaW46UGFzc3dvcmQ=" https://alpaca.vwave.net:8443/api/v1/clusters/Production/service-providers/vwave_sp/groups/ECG/device-type-custom-tag -d '{ "deviceType": "Polycom VVX 410 ", "tagName": "alpaca_test_tag", "tagValue": "test123" }'
-
Authorization: Basic
- This header can be used in place of passing a plain text username and password with each request. The value to pass here is the base64-encoded version ofusername:password
. - Example:curl -X POST --header "Authorization: Basic YWRtaW46UGFzc3dvcmQ=" https://{{alpaca_url}}:{{port}}/api/v1/{{endpoint}}'
Filters
Certain endpoints support filters. Filters are passed in as an array of query parameters on the request. The format is as follows:
`https://my-alpaca.com/api/v1/search?filters=KEY<DELIMITER>FILTER_TYPE<DELIMITER>VALUE,KEY2<DELIMITER>FILTER_TYPE2<DELIMITER>VALUE2...`
There are three parts to a filter:
- KEY: The variable to filter by.
- FILTER_TYPE: The type of filter. Valid values:
- IS: The equivalent of equals.
broadWorksId<DELIMITER>IS<DELIMITER>admin@lab.com
- IS_BOOLEAN: The equivalent of equals when the value is TRUE/FALSE.
disabled<DELIMITER>IS_BOOLEAN<DELIMITER>false
- IN: Used when the key should be in the value.
- Example:
type<DELIMITER>IN<DELIMITER>USER<DELIMITER>CALL_CENTER
- NIN: Used when the key should not be in the value.
- Example:
type<DELIMITER>NIN<DELIMITER>GROUP<DELIMITER>AUTO_ATTENDANT
- REGEX: Used to pattern match the key in the value. Currently, this acts as an includes.
- Example:
broadWorksId<DELIMITER>REGEX<DELIMITER>-admin
- BETWEEN: Used when comparing dates. This value should include two values.
- Example:
logDate<DELIMITER>BETWEEN<DELIMITER>2024-11-25T08:58:00.000<DELIMITER>2024-11-28T08:58:00.000
- VALUE: The value to match.
Note: Any special character within the value will need to be URL Encoded. Using the above between as example, this would become:
logDate<DELIMITER>BETWEEN<DELIMITER>2024-11-25T08%3A58%3A00.000<DELIMITER>2024-11-28T08%3A58%3A00.000
Getting Started with Alpaca Plugins
Spring
Alpaca is built upon the Spring Boot Framework. The Spring framework is an open source Java platform that provides
comprehensive infrastructure support for developing robust Java applications. See
the Spring documentation for help and tutorials regarding Spring specific
issues. In order for the plugins to be found by Spring the class must exist within a sub package of co.ecg
. Alpaca
scans any code found within those packages during the startup of the application. For example, a plugin located
within co.ecg.voicemail
would be found, whereas co.voicemail
would not.
Connecting to BroadWorks
Most plugins will want to make requests and receive responses from BroadWorks. This is done using the BroadWorksServer
object and its provider the BroadWorksConnectionService
. The BroadWorksConnectionService
is a service that can be
autowired into the plugin and provides methods to retrieve an active BroadWorksServer
object connected to an
underlying BroadWorks system. From there, requests or objects can be retrieved.
Provisioning
Alpaca provisioning is based around plugins that are attached to specific lifecycle stages. The provisioning lifecycle is a five-step process. To attach a plugin to a specific step of the process, a plugin class will implement the associated java interface.
-
Process
- Interface:
ProvisioningPreprocessor
- This step performs manipulation of the provided data to allow it to conform to standards and autofill fields with intelligent defaults. By default, Alpaca performs changes that aids the filling in of incomplete data. For example, if the device name field is missing it is populated with the MAC address if available.
- The first thing that occurs during this step is the insertion of variables onto the spreadsheet that are available
via the
getVariables()
method on theProvisioningCollection
. This Map of data can be used to store data for other steps or to retrieve default values. By default, the following keys are inserted with the value being the group default domain. The value of these keys can be overridden to change the default behavior for the values that were populated automatically.ProvisioningConstants.DEFAULT_DOMAIN
ProvisioningConstants.SIP_DEFAULT_DOMAIN
ProvisioningConstants.VOICEMAIL_DEFAULT_DOMAIN
- Interface:
-
Validate
- Interface:
ProvisioningValidator
- This step performs validation against the complete data. This is intended to verify that the data is complete and ready to be submitted to BroadWorks. Alpaca comes preloaded with validation that the BroadWorks required fields to create Users and devices exist within the supplied data. It also checks the availability of desired phone numbers, extension, MAC addresses, and services.
- Interface:
-
Pre-Service Provider Add
- Interface:
PreServiceProviderAddListener
- This step allows for any changes that need to be made immediately prior to adding Service Providers / Enterprises.
- Interface:
Create Service Providers / Enterprises - This step in the lifecycle creates the Service Providers / Enterprises within BroadWorks.
-
Post-Service Provider Add
- Interface:
PostServiceProviderAddListener
- This step is called upon the completion of the Service Provider / Enterprise creation.
- Interface:
-
Pre-Group Add
- Interface:
PreGroupAddListener
- This step allows for any changes that need to be made immediately prior to adding Groups.
- Interface:
Create Groups - This step in the lifecycle creates the Groups within BroadWorks.
-
Post-Group Add
- Interface:
PostGroupAddListener
- This step is called upon the completion of the Group creation.
- Interface:
-
Pre-Device Add
- Interface:
PreDeviceAddListener
- This step allows for any changes that need to be made immediately prior to a Device being created. This could be used for reaching out to external systems or for BroadWorks calls to prepare for a Device being added.
- Interface:
Create Devices - This step in the lifecycle creates the Device within BroadWorks.
-
Post-Device Add
- Interface:
PostDeviceAddListener
- This step is called upon the completion of the Device creation. Alpaca uses this step to configure the device's credentials that were supplied.
- Interface:
-
Pre-User Add
- Interface:
PreUserAddListener
- This step allows for any changes that need to be made immediately prior to a User being created. This could be used for reaching out to external systems or for BroadWorks calls to prepare for a User being added.
- Interface:
Create Users - This step in the lifecycle creates the User within BroadWorks. If a user is associated with a device it will be assigned during the user creation. Alpaca will then update the User's voice portal passcode, assign services, and assign service packs.
-
Post-User Add
- Interface:
PostUserAddListener
- This step is called upon the completion of the User creation. Alpaca uses this step to configure the Authentication service with supplied credentials. It also configures and enables voice messaging if the service has been assigned. Some potential uses are to reach out and create a voicemail box on a third party mail platform or configure custom announcements. This is called once per User within a provisioning task.
- Interface:
-
Pre Call Center Add
- Interface:
PreCallCenterAddListener
- This step is called after Groups and Users have been created but before adding Call Centers.
- Interface:
-
Pre Hunt Group Add
- Interface:
PreHuntGroupAddListener
- This step is called after Groups and Users have been created but before adding Hunt Groups.
- Interface:
-
Provisioning Complete
- Interface:
ProvisioningCompleteListener
- This final step is called after all objects have been created. This step provides for a final lifecycle point to make changes or outbound requests in a single spot rather than being called repeatedly for each user. For example, a notification could be sent to other outside systems that the provisioning had been completed. This is called once per task.
- Interface:
Much like the ProvisioningCollection
, each row of data held in the list of UserProvisioningDTO
objects also has an
attribute map. This can be used to pass data between steps for a specific row. The default implementation uses this map
to mark if a User's selected device already exists so that later steps know whether to perform assignment or the
creation of a new device.
In addition to the implemented interface at the desired lifecycle step, a provisioning plugin must contain the
annotation @ProvisioningComponent
. This designates the plugin as one intended to be used by the Alpaca provisioning
service and registers it with Spring. It also contains descriptors and priority control to allow for fine-tuning the
order in which plugins are run. Priority for plugins is handled from lowest to highest. For example, a plugin with
priority 0 will be run before a plugin with priority 25. The default priority is 50.
@ProvisioningComponent(
priority = 5,
description = "My Provisioning Plugin",
name = "ProvisioningPlugin"
)
When retrieving a `BroadWorksServer` from the "Process" or "Validate" step, use the `getConnection(cluster)` method on
the `BroadWorksConnectionService`. For the "Pre-Add" and subsequent steps in the Alpaca provisioning lifecycle, a
special method is required to ensure that the correct BroadWorks user is used for the outbound connection. In these
scenarios, use the `getProvisioningConnection(task, user)` method.
Putting all of the above rules into place, we can now create a simple provisioning plugin:
// Within the co.ecg namespace so that it will be found by Spring
package co.ecg.alpaca.plugins;
// Signifies that this is an Alpaca plugin and registers it with Spring
@ProvisioningComponent(
priority = 5,
description = "Plugin to add Phone Numbers prior to User provisioning",
name = "PhoneNumberPlugin"
)
public class PhoneNumberPreUserAddListener implements PreUserAddListener {
// Spring annotation to supply the connection service
@Autowired
BroadWorksConnectionService broadWorksConnectionService;
// The interface method for pre-add step
@Override
public void preUserAdd(TaskDTO task, UserProvisioningDTO user, Logger logger) throws ProvisioningException {
log.info("Beginning Phone Number Pre User Add.");
// Retrieve BroadWorks Server
BroadWorksServer broadWorksServer =
broadWorksConnectionService.getProvisioningConnection(task, user);
// Perform Plugin Requests
}
}
Delete Plugins
Custom plugins can be hooked up during the Alpaca Destroy lifecycles.
The available plugins are as follows:
-
Pre Service Provider Delete
- Interface:
PreServiceProviderDeleteListener
- This step is called prior to deleting a Service Provider/Enterprise during a Service Provider Destroy task.
- Interface:
-
Post Service Provider Delete
- Interface:
PostServiceProviderDeleteListener
- This step is called after deleting a Service Provider/Enterprise during a Service Provider Destroy task.
- Interface:
-
Pre Group Delete
- Interface:
PreGroupDeleteListener
- This step is called prior to deleting a Group during a Group Destroy task.
- Interface:
-
Post Group Delete
- Interface:
PostGroupDeleteListener
- This step is called after deleting a Group during a Group Destroy task.
- Interface:
-
Pre User Delete
- Interface:
PreUserDeleteListener
- This step is called prior to deleting an User during a User Destroy task or Bulk User Delete task.
- Interface:
-
Post User Delete
- Interface:
PostUserDeleteListener
- This step is called after deleting an User during a User Destroy task or Bulk User Delete task.
- Interface:
Migration Transformation
Transforms plugins extend Alpaca's migration framework. Transformations happen before the migration target is moved to its destination.
- Transforms must include the annotations
@Component
and@TransformComponent
- The transform class should implement the interface for the type of migration the transform will occur during a ServiceProviderMigrationTransform, UserMigrationTransform, etc.
- The transform method will need to be implemented. After implementing the transform method, the project is ready to build.
@TransformComponent(priority = 15)
@Component
public class MyBroadWorksClusterTransform> implements ServiceProviderMigrationTransform {
@Override
public T transform(BroadWorksProcess process, BroadWorksServer destination, T information, boolean isAdoptDestinationDefaultDomain, String newId) {
// Retrieve BroadWorks Server
BroadWorksServer broadWorksServer = process.getBroadWorksServer();
// Perform Transform
...
return information;
}
}
Custom Plugins
Custom plugins are decoupled from any other Alpaca process. They can be run from the 'Action' menu from the Cluster, Service Provider, Group, and User places. The location that the plugin is run from will be passed into the plugin's code. Custom parameters can be passed into the plugin as key-value pairs. The custom plugin can then reference and use these parameters.
customPluginsEnabled
must be set to true in the Alpaca Client Configuration.
-
Process:
- Plugin class must include the
@CustomPluginComponent
annotation.- The
name
field must be provided. - A
description
field can also be provided. @CustomPluginComponent(name = "My Custom Plugin", description = "This is my first custom plugin.")
- The
- Plugin class most implement the
CustomPlugin
interface.- The
execute(SearchIndex location, Map<String, String> parameters)
method must be implemented.-
SearchIndex location
- Database object for the location the plugin is run from. -
Map<String, String> parameters
- Map of key-value pairs for provided custom parameters. - Returns
List<String>
. This is intended to be a list of messages that will be displayed to the end user upon completion. - Must throw
PluginException
-
- The
- Like provisioning plugins, custom plugins can take full advantage of Spring's Autowire framework. Please reference the Alpaca JavaDocs for available classes.
- Custom plugins must be packaged as
.jar
files and places in/opt/alpaca-server-*/plugins
.- Once loaded, Alpaca will need to be restarted.
- You can confirm your plugin is loaded by looking for the following line in the
alpaca-server-*.log
file.Registering custom plugins.
- Plugin class must include the
Example:
@CustomPluginComponent(name = "Custom Plugin One", description = "This is a JAR plugin")
public class CustomPluginOne implements CustomPlugin {
@Override
public List execute(SearchIndex location, Map parameters) throws PluginException {
List results = new ArrayList<>();
try {
// Perform Plugin Logic
} catch (Exception ex) {
throw new PluginException("An error happened during execution!", ex);
}
return results;
}
}
Alpaca Concepts
Alpaca Properties
All Alpaca Properties located in the application-prod.yml
can be accessed via the AlpacaProperties
class.
@Component
public class MyAlpacaClass {
@Autowired
AlpacaProperties alpacaProperties;
public MyAlpacaClass() {
// Constructor
}
public void myMethod() {
// Retrieve auditLog.retentionDays property
int retentionDays = alpacaProperties.getAuditLogs().getRetentionDays();
// Do something with retrieved property
}
}
Firing Requests
All OCI Requests have a corresponding Java class that can be created. To get all the Access Devices in a BroadWorks
system you would need to send a SystemAccessDeviceGetAllRequest
. In Alpaca this would be accomplished by calling:
SystemAccessDeviceGetAllRequest accessDeviceGetAllRequest = new SystemAccessDeviceGetAllRequest(broadWorksServer);
All required fields for the request will be in the constructor. However, many OCI requests have optional fields that can
be accessed by calling .setX(Y)
on the Request
object prior to sending it to BroadWorks.
After the Request
has been created it can be sent to the system by calling .fire()
. For GET type requests they will
return a specific Response
.
SystemAccessDeviceGetAllResponse accessDeviceGetAllResponse = accessDeviceGetAllRequest.fire();
Requests can also be fired asynchronously by calling .asyncFire()
and passing in the function to call on completion.
accessDeviceGetAllRequest.asyncFire(response -> {
response.getAccessDeviceTable();
});
Request Per Object Producer
The Request Per Object Producer performs a Request across a list of BroadWorksObjects. The Request Per Object Producer
lives in the RequestHelper
class.
public List retrieveBLFUriForUsers(BroadWorksServer broadWorksServer, List users) {
// Create list for uris
List userBLFUriList = new ArrayList<>();
try {
// Call requestPerObjectProducer
RequestHelper.requestPerObjectProducer(
// Pass in BroadWorks Server
broadWorksServer,
// Pass in List of Users
users,
// Type of item in the list
User.class,
// Request to perform
UserBusyLampField.UserBusyLampFieldGetRequest.class,
// Consumer
(user, response) -> {
if(!response.isErrorResponse() && response.getListURI() != null) {
userBLFUriList.add(response.getListURI());
}
},
// Expected Error Codes - This particular error code is "Service not assigned"
"4410");
} catch (RequestException ex) {
log.error("Error while retrieving blf uris", ex);
}
// Return list
return userBLFUriList;
}
Request Contexts
Request contexts can be used to fire requests asynchronously.
// Create BroadWorksProcess
BroadWorksProcess process = new BroadWorksProcess(getBroadWorksServer());
// Get Request Context
RequestContext context = process.getNewRequestContext();
// Fire Request Asynchrounously
context.put(new Request, response -> {
// Consumer
if (process.isError(response)) {
// Do something
}
});
// Fire Request Asynchrounously
context.put(new DifferentRequest, response -> {
// Consumer
if (process.isError(response)) {
// Do something else
}
});
// Wait for all requests to finish
context.join();
Information Builders
Information builders are a set of classes used to retrieve information about a BroadWorksObject. Information builders can be used to retrieve all information about a BroadWorks Object or just a subset of information based on your needs. Information Builders are available for Users, Groups, Enterprises, ServiceProviders, AccessDevice, and all service instances.
Retrieve All Information
// Create a process to use for the builder
BroadWorksProcess broadWorksProcess = new BroadWorksProcess(broadWorksServer);
// Retrieve User from BroadWorks
User user = User.getPopulatedUser(broadWorksServer, userId);
// Build All Information For User
UserInformation userInformation = new UserInformationBuilder(broadWorksProcess, user).all().execute();
// Retrieve User Schedules from Information
List schedules = userInformation.getUserSchedules();
// Perform action with retrieved information
...
Retrieve Needed Information
// Create a process to use for the builder
BroadWorksProcess broadWorksProcess = new BroadWorksProcess(broadWorksServer);
// Retrieve User from BroadWorks
User user = User.getPopulatedUser(broadWorksServer, userId);
// Build Only Schedule and Service Information For User - Note all other information will be null
UserInformation userInformation = new UserInformationBuilder(broadWorksProcess, user).addUserSchedules().addUserServices().execute();
// Retrieve User Schedules from Information
List schedules = userInformation.getUserSchedules();
// Perform action with retrieved information
...