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:
    1. Post to the login endpoint - /api/login - passing username and password as a body object.
    2. If credentials are valid, three headers will be returned
      • SESSION
      • XSRF-TOKEN
      • X-Alpaca-Id
    3. These headers will need to be passed along for subsequent requests.
cUrl Example
  • Retrieve list of Clusters

    1. 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/

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 is application/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 of username: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.

  1. 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 the ProvisioningCollection. 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
  2. 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.
  3. Pre-Service Provider Add

    • Interface: PreServiceProviderAddListener
    • This step allows for any changes that need to be made immediately prior to adding Service Providers / Enterprises.
  4. Create Service Providers / Enterprises - This step in the lifecycle creates the Service Providers / Enterprises within BroadWorks.

  5. Post-Service Provider Add

    • Interface: PostServiceProviderAddListener
    • This step is called upon the completion of the Service Provider / Enterprise creation.
  6. Pre-Group Add

    • Interface: PreGroupAddListener
    • This step allows for any changes that need to be made immediately prior to adding Groups.
  7. Create Groups - This step in the lifecycle creates the Groups within BroadWorks.

  8. Post-Group Add

    • Interface: PostGroupAddListener
    • This step is called upon the completion of the Group creation.
  9. 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.
  10. Create Devices - This step in the lifecycle creates the Device within BroadWorks.

  11. 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.
  12. 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.
  13. 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.

  14. 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.
  15. Pre Call Center Add

    • Interface: PreCallCenterAddListener
    • This step is called after Groups and Users have been created but before adding Call Centers.
  16. Pre Hunt Group Add

    • Interface: PreHuntGroupAddListener
    • This step is called after Groups and Users have been created but before adding Hunt Groups.
  17. 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.

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:

  1. Pre Service Provider Delete

    • Interface: PreServiceProviderDeleteListener
    • This step is called prior to deleting a Service Provider/Enterprise during a Service Provider Destroy task.
  2. Post Service Provider Delete

    • Interface: PostServiceProviderDeleteListener
    • This step is called after deleting a Service Provider/Enterprise during a Service Provider Destroy task.
  3. Pre Group Delete

    • Interface: PreGroupDeleteListener
    • This step is called prior to deleting a Group during a Group Destroy task.
  4. Post Group Delete

    • Interface: PostGroupDeleteListener
    • This step is called after deleting a Group during a Group Destroy task.
  5. Pre User Delete

    • Interface: PreUserDeleteListener
    • This step is called prior to deleting an User during a User Destroy task or Bulk User Delete task.
  6. Post User Delete

    • Interface: PostUserDeleteListener
    • This step is called after deleting an User during a User Destroy task or Bulk User Delete task.

Migration Transformation

Transforms plugins extend Alpaca's migration framework. Transformations happen before the migration target is moved to its destination.

  1. Transforms must include the annotations @Component and @TransformComponent
  2. The transform class should implement the interface for the type of migration the transform will occur during a ServiceProviderMigrationTransform, UserMigrationTransform, etc.
  3. 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.")
    • 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
    • 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.
  • 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
    ...