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 of 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
    3. SESSION
    4. XSRF-TOKEN
    5. 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 test 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}}'

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 it's 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 values auto populated.
      • 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 pre-loaded 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-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.
  4. Create Devices - This step in the lifecycle creates the Device within BroadWorks.

  5. 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.
  6. 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.
  7. 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.

  8. 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.
  9. 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 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) throws ProvisioningException {
        
        // Retrieve BroadWorks Server
        BroadWorksServer broadWorksServer = 
            broadWorksConnectionService.getProvisioningConnection(task, user);
        
        // Perform Plugin Requests
    }
}

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 i.e. 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;
    }
}

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 request 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
    ...