Metaswitch Call Notification REST API

The Rhino REST API Framework was used to create the MSW-CN REST API implementation (Creating a REST API). The generated module structure is:

Generated MSW-CN module structure
msw-call-notification-rest-api 1
msw-call-notification-rest-api/msw-call-notification-api/src/api.yaml 2
msw-call-notification-rest-api/oma-call-notification-api/src/api.properties 3
msw-call-notification-rest-api/msw-call-notification-api 4
msw-call-notification-rest-api/msw-call-notification-api-ratype 5
msw-call-notification-rest-api/msw-call-notification-api-plugin 6
msw-call-notification-rest-api/msw-call-notification-api-client-sbbpart 7
  1. The MSW-CN group module

  2. The MSW-call-notification-api sub-module holds the API specification and a properties file. The other sub-modules have an ivy dependency on this module.

  3. An OpenAPI formatted description of the MSW-CN API

  4. A properties file that defines java packages, and the names of deployable units

  5. Generates a RA type that includes events, provider interfaces and so on for a MSW-CN RA Type that can be deployed in Rhino

  6. Generates a library component the Unified REST RA uses to load and drive the MSW-CN API

  7. Generates a Jar containing a super-class that developers extend to create an sbb-part, for the client MSW-CN role, in their application. The generated super-class includes all event handlers and slee-annotations required to deploy such an sbb-part in Rhino

Tip Developers may create multiple REST API modules. A REST RA can support one or more such REST APIs.

Call Notification Resource Adaptor

The Rhino REST API Framework was used to create the Call Notification Resource Adaptor (Creating a REST Resource Adaptor). The REST Demonstration REST resource adaptor module is: call-notification-ra

The call-notification-ra is an Ant/Ivy build module that depends on the Unified REST RA Core and the Metaswitch Call Notification REST API. The call-notification-ra produces a Unified REST RA, that supports MSW-CN, used in Sentinel-SIP.

Tip Developers may create multiple REST RA modules, each with different sets of APIs to suit different applications.

Call Notification Client Event Handler

The MSW-CN event handler sbbpart (rest-demo-feature-modules/msw-call-notification-client-event-handler) contains an sbb-part that extends the class generated in msw-call-notification-rest-api/msw-call-notification-api-client-sbbpart. The MswCallNotificationClientEventHandlerSbbpart class extends the MswCallNotificationApiClientSbbpart class and implements behaviour on receipt of call direction and call event notification response events.

The super class includes the required SBBPart, RATypeBinding and EventMethod slee annotations.

The MswCallNotificationClientEventHandlerSbbpart event handler sbb-part
package com.opencloud.openapi.demo;

// msw call notification API
import com.opencloud.openapi.demo.msw_cn.api.*;

// super-class for an sbb-part to process
// msw call notification related response events
import com.opencloud.openapi.demo.msw_cn.impl.*;

// to bind the event handler sbbpart to the sentinel-sip service
@BinderTargets(services="sip")
public class MswCallNotificationClientEventHandlerSbbpart extends MswCallNotificationClientSbbpart {

    public MswCallNotificationClientEventHandlerSbbpart(SbbPartContext context) { super(context); }

    @Override 1
    protected void handleCallDirectionNotification_200_SuccessResponse(
            CallDirectionNotification_200_SuccessResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallDirectionNotification", response, response.getStatusCode(),
            "SuccessResponse", aci, eventContext);
    }

    @Override 2
    protected void handleCallDirectionNotificationDefaultResponse(
            CallDirectionNotificationDefaultResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallDirectionNotification", response, response.getStatusCode(),
            "DefaultResponse", aci, eventContext);
    }

    @Override 3
    protected void handleCallEventNotification_200_SuccessResponse(
            CallEventNotification_200_SuccessResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallEventNotification", response, response.getStatusCode(),
            "SuccessResponse", aci, eventContext);
    }

    @Override 4
    protected void handleCallEventNotificationDefaultResponse(
            CallEventNotificationDefaultResponse response,
            ActivityContextInterface aci,
            EventContext eventContext) throws IOException
    {
        submitResponseEventToSentinel(
            "CallEventNotification", response, response.getStatusCode(),
            "DefaultResponse", aci, eventContext);
    }

    // Submit response event to sentinel runtime
    // for delivery to a feature for processing
    private void submitResponseEventToSentinel(
            String operation, Object response, int statusCode,
            String description,
            ActivityContextInterface aci, EventContext eventContext)
    {
        final SentinelActivityContextInterface sentinelAci =
            getSbbPartContext().asSbbPartActivityContextInterface(aci);

        final String target = sentinelAci.getTargetFeature();  5
        if (target == null || target.isEmpty()) {
            getTracer().fine("No target feature for response, ignore");
            return;
        }

        getTracer().finer("Pass {} {} {} to target feature \"{}\"",
            operation, statusCode, description, target);
        try {
            SentinelEndpoint sentinelEndpoint =
                (SentinelEndpoint) getSbbPartContext().getSbbLocalObject();
            sentinelEndpoint.processEvent(
                    target, response, false, true, aci, eventContext); 6
        }
        catch (SentinelFireEventException | IllegalArgumentException | NullPointerException e)
        {
            getTracer().severe("Exception processing {} {} {} event",
                operation, statusCode, description, e);
        }
    }
}
  1. override method in MswCallNotificationClientSbbpart to handle a CallDirectionNotification 200 Success response

  2. override method in MswCallNotificationClientSbbpart to handle a CallDirectionNotification default response (i.e all other CallDirectionNotification responses)

  3. override method in MswCallNotificationClientSbbpart to handle a CallEventNotification 200 Success response

  4. override method in MswCallNotificationClientSbbpart to handle a CallEventNotification default response (i.e all other CallEventNotification responses)

  5. find the feature the response event should be processed by

  6. submit the event to the Sentinel-SIP runtime for processing by the target feature

Set Session Plan Feature

The RestSetSessionPlan feature (rest-demo-feature-modules/msw-set-session-plan-feature) updates the sentinel selection key PlanID field with the REST Demonstration session plan ID.

Tip Learn about the Sentinel Selection Key and read Features for an introduction to Sentinel features.
The RestSetSessionPlan feature
package com.opencloud.openapi.demo;

@SentinelFeature(
    featureName = RestSetSessionPlan.NAME,
    componentName = "@component.name@",
    featureVendor = "@component.vendor@",
    featureVersion = "@component.version@",
    featureGroup = SentinelFeature.SIP_FEATURE_GROUP,
    executionPhases = ExecutionPhase.SipSessionPhase,
    usageStatistics = RestSetSessionPlanStats.class
)
// to bind the feature to the sentinel-sip service
@BinderTargets(services = "sip")
public class RestSetSessionPlan
        extends BaseFeature<SentinelSipSessionState, SentinelSipMultiLegFeatureEndpoint>
        implements InjectFeatureStats<RestSetSessionPlanStats>
{
    public static final String NAME = "RestSetSessionPlan";

    // ...

    @Override
    public void startFeature(Object trigger, Object activity, ActivityContextInterface aci) {
        final SentinelSelectionKey selectionKey = getSessionState().getSentinelSelectionKey();
        // Set the plan ID in the selectionKey to 'rest_demo'
        selectionKey.setPlanId(REST_DEMO_SESSION_PLAN);
        getTracer().finest("SentinelSelectionKey set: {}", selectionKey);
        getSessionState().setSentinelSelectionKey(selectionKey);
        getCaller().featureHasFinished();
    }

    private static final String REST_DEMO_SESSION_PLAN = "rest_demo";
}

The value of the SentinelSelectionKey is used by the Sentinel run-time to resolve feature configuration, and the feature execution scripts that run.

Note There are many alternate approaches to deriving a plan ID. For example the plan ID could be derived by analysing headers in the SIP INVITE.
Tip Session plans, the Sentinel-SIP session plan and feature execution scripts are explained here: Session Plans, SIP Sessions Initiated via SIP INVITE and Feature Execution Scripts.

Send Call Notification Feature

The RestSendCallNotification feature (rest-demo-feature-modules/msw-send-call-notification) sends CallDirection and CallEvent notification requests and processes the corresponding responses. The RestSendCallNotification feature:

  • determines the type of notification event to send, if appropriate, based on the point-in-session and the triggering SIP event.

  • will influence the current SIP session based on CallDirection response events. For example the session may be ended, routed to an alternate destination or allowed to continue.

startFeature method of the RestSendCallNotification feature
@Override
public void startFeature(Object trigger,
                         Object activity,
                         ActivityContextInterface aci)
{
    // Find the active point-in-session 1
    final SipFeatureScriptExecutionPoint executionPoint =
        getSessionState().getCurrentSipFeatureExecutionPoint();

    if (trigger instanceof IncomingSipRequest
        || trigger instanceof IncomingSipResponse)
    {
        // Determine the notification type and call event 2
        final CallNotificationTypes notificationType;
        final CallEvents callEvent;
        if (trigger instanceof IncomingSipRequest) {
            final IncomingSipRequest request = (IncomingSipRequest) trigger;
            notificationType = determineNotificationType(
                                    executionPoint, request);
            callEvent = determineCallEvent(executionPoint, request);
        }
        else {
            final IncomingSipResponse response  = (IncomingSipResponse) trigger;
            notificationType = determineNotificationType(
                                    executionPoint, response);
            callEvent = determineCallEvent(executionPoint, response);
        }

        if (null == notificationType || null == callEvent) {
            getCaller().featureHasFinished();
            return;
        }

        final LegManager lm = getCaller().getLegManager();
        final Leg calledPartyLeg = lm.getCallingPartyLeg().getLinkedLeg();

        // if there is no linked leg, then this response will have been received
        // on a leg associated with an MRF (for an announcement).
        // A notification is not required in this case.
        if (null == calledPartyLeg) {
            final Leg leg = getCaller().getLegManager().getLeg(aci);
            getTracer().finest("A notification is not required at: {} for response on leg {}",
                                    executionPoint, leg.getLegName());
            getCaller().featureHasFinished();
            return;
        }

        // send a notification 3
        // if the leg is not active, a sip session has not been created yet
        // if there is no INVITE request, then an INVITE has not
        // been sent on the cld leg yet
        if (calledPartyLeg.isActive()
                && null != calledPartyLeg.getInviteRequest())
        {
            sendNotification(calledPartyLeg, notificationType, callEvent);
        }
        else {
            sendNotification(aci, notificationType, callEvent);
        }
    }
    else {
        // Process a notification response event 4
        if (processNotificationResponse(
                executionPoint, trigger, activity, aci))
        {
            getCaller().featureHasFinished();
        }
        getCaller().detach(aci);
    }
}
  1. Find the currently execution feature script execution point.

  2. Determine the notification type and call event based on the triggering SIP message, and the point-in-session

  3. Send a notification request message

  4. Process a CallDirection or CallEvent notification response event.

Determining the MSW-CN notification event to send

// Determine the notification type from the triggering SIP request 1
private CallNotificationTypes determineNotificationType(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipRequest request)
{
    switch (executionPoint) {
        // the initial trigger
        case SipAccess_SessionStart:
            return CallNotificationTypes.CALLDIRECTION;
        // one of the participants hangs up
        case SipMidSession_PartyRequest:
            return "BYE".equals(request.getMethod())
                        ? CallNotificationTypes.CALLDIRECTION : null;
        // a notification is not required
        default: return null;
    }
}

// Determine the notification type from the triggering SIP response 2
private CallNotificationTypes determineNotificationType(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipResponse response)
{
    switch (executionPoint) {
        case SipAccess_PartyResponse:
            if (! "INVITE".equals(response.getRequest().getMethod()))
                return null;
            switch (response.getStatus()) {
                case SipResponse.SC_OK:
                    return CallNotificationTypes.CALLEVENT;
                // 486 - called party busy
                case SipResponse.SC_BUSY_HERE:
                // 404 - no route to destination
                case SipResponse.SC_NOT_FOUND:
                // 408 - no answer
                case SipResponse.SC_REQUEST_TIMEOUT:
                // 480 - no answer
                case SipResponse.SC_TEMPORARLY_UNAVAILABLE:
                    return CallNotificationTypes.CALLDIRECTION;
                // a notification is not required
                default: return null;
            }

        case SipMidSession_PartyResponse:
            if (SipResponse.SC_OK == response.getStatus()
                && "BYE".equals(response.getRequest().getMethod()))
                return CallNotificationTypes.CALLEVENT;
            // otherwise a notification is not required
            return null;

        // a notification is not required
        default: return null;
    }
}
  1. At SipAccess_SessionStart Sentinel-SIP is processing a triggering INVITE request, so send a CallNotificationTypes.CALLDIRECTION request. At SipMidSession_PartyRequest we are interested in BYE requests and send a CallNotificationTypes.CALLDIRECTION request.

  2. At SipAccess_PartyResponse we are interested in responses to the triggering INVITE request. On answer, send a CallNotificationTypes.CALLEVENT request, otherwise send a CallNotificationTypes.CALLDIRECTION request. At SipMidSession_PartyResponse we are interested in responses to BYE requests and send a CallNotificationTypes.CALLEVENT request.

// Determine the CallEvent from the triggering SIP request 1
private CallEvents determineCallEvent(
                        SipFeatureScriptExecutionPoint executionPoint,
                        IncomingSipRequest request)
{
    switch (executionPoint) {
        // the initial trigger
        case SipAccess_SessionStart:
            return CallEvents.CALLEDNUMBER;
        // one of the participants hangs up
        case SipMidSession_PartyRequest:
            return "BYE".equals(request.getMethod())
                        ? CallEvents.DISCONNECTED : null;
        // a notification is not required
        default: return null;
    }
}

// Determine the CallEvent from the triggering SIP response 2
private CallEvents determineCallEvent(
            SipFeatureScriptExecutionPoint executionPoint,
            IncomingSipResponse response)
{
    switch (executionPoint) {
        case SipAccess_PartyResponse:
            if (! "INVITE".equals(response.getRequest().getMethod()))
                return null;
            switch (response.getStatus()) {
                case SipResponse.SC_OK:
                    return CallEvents.ANSWER;
                // 486 - called party busy
                case SipResponse.SC_BUSY_HERE:
                    return CallEvents.BUSY;
                // 404 - no route to destination
                case SipResponse.SC_NOT_FOUND:
                    return CallEvents.NOTREACHABLE;
                // 408 - no answer
                case SipResponse.SC_REQUEST_TIMEOUT:
                // 480 - no answer
                case SipResponse.SC_TEMPORARLY_UNAVAILABLE:
                    return CallEvents.NOANSWER;
                // a notification is not required
                default: return null;
            }
        case SipMidSession_PartyResponse:
            if (SipResponse.SC_OK == response.getStatus()
                && "BYE".equals(response.getRequest().getMethod()))
                return CallEvents.DISCONNECTED;
            // otherwise a notification is not required
            return null;

        // a notification is not required
        default: return null;
    }
}
  1. At SipAccess_SessionStart Sentinel-SIP is processing a triggering INVITE request (a CallEvents.CALLEDNUMBER event). At SipMidSession_PartyRequest we are interested in BYE requests (CallEvents.DISCONNECTED events)

  2. At SipAccess_PartyResponse we are interested in responses to the triggering INVITE request (CallEvents.ANSWER, CallEvents.BUSY and so on). At SipMidSession_PartyResponse we are interested in responses to BYE requests (CallEvents.DISCONNECTED events).

Sending CallDirection and CallEvent notification requests

The RestSendCallNotification feature sends CallDirection and CallEvent notification requests in the following way.

sendNotification private method of the RestSendCallNotification feature
// Send a call event notification
private void sendNotification(
        String from, String fromDisplayName, String to,
        CallNotificationTypes notificationType, CallEvents callEvent)
{
    final SendCallNotificationConfig configuration =
            configurationReader.getConfiguration(
                    getSessionState().getSentinelSelectionKey());

    final ApiConfiguration apiConfiguration =
            ApiConfiguration.standardConfiguration();
    apiConfiguration.set(ApiOptions.destinationUrl,
                         configuration.getDestinationUrl());
    final MswCallNotificationRaTypeApiClient client =
            cnProvider.getApiClient(apiConfiguration);

    // Create a new CallEventNotification object 1
    final CallEventNotification notification = new CallEventNotification();
    try {
        notification.notificationType(notificationType)
            .eventDescription(new EventDescription().callEvent(callEvent))
            .calledParticipant(new URI(to))
            .callingParticipant(new URI(from))
            .callingParticipantName(fromDisplayName);
    }
    catch (URISyntaxException e) {
        getTracer().severe(
            "Failed to extract participants from: {}, to: {}", from, to, e);
    }

    // Create a REST request to send 2
    final NotificationApi nApi = client.getNotificationApi();
    final RestRequestBuilder notificationRequest =
            notificationType == CallNotificationTypes.CALLEVENT
            ? nApi.createCallEventNotificationRequest(notification)
            : nApi.createCallDirectionNotificationRequest(notification);

    try {
        // Send the REST request 3
        final OutgoingRestActivity outgoingRestActivity =
            notificationApi.sendRequest(notificationRequest);
        final ActivityContextInterface newAci =
            cnACIFactory.getActivityContextInterface(outgoingRestActivity);
        final SentinelActivityContextInterface outgoingAci =
            getCaller().asSentinelActivityContextInterface(newAci);
        stats.incrementSendCallNotification(1);

        // Set the target feature to 'RestSendCallNotification' 4
        outgoingAci.setTargetFeature(NAME);
        // Attach to the new activity, wait for the response 5
        outgoingAci.attach(getFacilities().getSbbLocalObject());
        getTracer().finest("Notification sent. Waiting for response");
        getCaller().featureWaiting(outgoingAci);
    }
    catch (IOException e) {
        getCaller().featureFailedToExecute(
                new FeatureError(FeatureError.Cause.unclassified,
                    "Failed to send notification", e));
    }
}
  1. The CallEventNotification has:

    • a notification type (CallNotificationTypes.CALLEVENT or CallNotificationTypes.CALLDIRECTION)

    • a call event (CallEvents.CALLEDNUMBER, CallEvents.ANSWER and so on)

    • called participant (the To address), calling participant (the From address) and the calling participant display name

  2. Create an appropriate REST request message by using the MSW-CN NotificationApi.

  3. Send the REST request. The Unified REST RA returns an OutgoingRestActivity related to the sent REST request.

  4. Set TargetFeature on the ActivityContextInterface to RestSendCallNotification so Sentinel-SIP will route the corresponding response to the currently executing feature.

  5. Attach to the activity and tell Sentinel-SIP that we are waiting for a response. Sentinel-SIP pauses executing the active feature execution script until a response event arrives.

Processing CallDirection 200 Success response events

A CallDirectionNotification success response includes instructions that must be followed to effect ongoing call processing.

private boolean processDirectionNotificationSuccessResponse(
            SipFeatureScriptExecutionPoint executionPoint,
            CallDirectionNotification_200_SuccessResponse trigger,
            Object activity)
{
    // Determine the action to perform 1
    final Action actionToTake = trigger.getContent();
    if (null == actionToTake || null == actionToTake.getActionToPerform()) {
        stats.incrementCallDirectionResponseWithNoActionToTake(1);
        return true;
    }

    // Apply the requested Action 2
    switch(actionToTake.getActionToPerform()) {
        case ROUTE:
            // Get the new destination 3
            final URI newDestination  = actionToTake.getRoutingAddress();
            if (null == newDestination) {
                stats.incrementCallDirectionNoRoutingAddress(1);
                return true;
            }
            final org.jainslee.resources.sip.URI sipDestination =
                getSipUri(newDestination);
            if (null == sipDestination) {
                stats.incrementCallDirectionNonSipRoutingAddress(1);
                return true;
            }
            stats.incrementCallDirectionRoute(1);
            // route the call to the new destination 4
            routeToNewDestination(executionPoint, sipDestination);
            return true;

        case CONTINUE:
            // Allow the call to continue without interruption 5
            stats.incrementCallDirectionContinue(1);
            return true;

        case ENDCALL:
            // End the call 6
            stats.incrementCallDirectionEndCall(1);

            // Check to see if an annoucement should be played 7
            final String announcementIdStr = actionToTake.getAnnouncementId();
            if (null == announcementIdStr) {
                getTracer().fine("Action is to ENDCALL");
                // defer to the leg manager to end the session
                final LegManager lm = getCaller().getLegManager();
                lm.endSession(SipResponse.SC_FORBIDDEN);
            }
            else {
                // Schedule an announcement 8
                try {
                    // announcement ID should be a Long.
                    final long announcementId = Long.parseLong(announcementIdStr);
                    getTracer().fine("Action is to ENDCALL after playing announcement: {}",
                                        announcementIdStr);
                    // tell sentinel-sip about the announcement to be played
                    final List<SipAnnouncementInformation> announcementQueue =
                                getSessionState().getEarlyMediaAnnouncementInfoQueue();
                    announcementQueue.add(SipAnnouncementInformation.getBuilder()
                                .setAnnouncementId(announcementId).build());
                    getSessionState().setEarlyMediaAnnouncementInfoQueue(announcementQueue);
                    // tell sentinel-sip to end the session after the announcement
                    getSessionState().setEndSessionAfterAnnouncement(SipResponse.SC_FORBIDDEN);
                    // used in rest-demo feature execution scripts
                    getSessionState().setPlayAnnouncementThenEndSession(true);
                }
                catch (NumberFormatException nfe) {
                    getTracer().fine("Announcement id: {} is not a valid. Ending the call.", announcementIdStr);
                    // defer to the leg manager to end the session
                    final LegManager lm = getCaller().getLegManager();
                    lm.endSession(SipResponse.SC_FORBIDDEN);
                }
            }
            // generate a DISCONNECTED notification 9
            sendNotification(
                    lm.getCallingPartyLeg(),
                    CallNotificationTypes.CALLEVENT,
                    CallEvents.DISCONNECTED);
            return false; // sent a notification, feature has not finished
    }
    return true;
}
  1. The CallDirectionNotification 200 success response body contains an Action object that dictates how the active SIP session should be processed.

  2. The ActionToPerform is one of ROUTE, CONTINUE or ENDCALL

  3. If the action to perform is ROUTE the actionToTake will also include a new destination address.

  4. Route the call to the new adddress.

  5. If the action to perform is CONTINUE allow the call to continue uninterrupted.

  6. If the action to perform is ENDCALL delegate to Sentinel-SIP.

  7. The actionToTake may, optionally, include an announcement ID

  8. If actionToTake includes an announcement ID, then update session state, so the PlayAnnouncement feature can play the announcement

  9. Generate a DISCONNECTED notification

Tip Learn more about the SIP Play Announcement Feature.

Feature Execution Scripts

The RestSetSessionPlan feature (rest-demo-feature-modules/msw-set-session-plan-feature) updates the sentinel selection key to with the REST Demonstration session plan. Sentinel-SIP uses the sentinel-selection-key when resolving the feature execution scripts to run at each point in session.

Tip For more details on the sentinel selection key, feature execution scripts and Sentinel-SIP feature script execution points see: Sentinel Selection Key Feature Execution Scripts and Sentinel SIP Feature Execution Points

The following feature execution scripts have been added for the REST Demonstration.

Point-in-Session feature Execution Script

SipAccess_SessionStart:::

featurescript RestSessionStart {
    if not LegManager.endSession
       and not LegManager.detachAll
       and not LegManager.currentLeg.releaseLeg
    {
        run AcceptSip
        run RestSetSessionPlan
        run DoNotChargeSipSession
    }
}

post:SipAccess_SessionStart:sipcall:rest_demo:

featurescript DemoPostSessionStart {
    if not LegManager.endSession {
        run RecordTimestamps mode "outbound"
        run RestSendCallNotification
        if session.PlayAnnouncementThenEndSession {
            run SipPlayAnnouncement
        }
    }
    run DetermineCauseCode
    run DiameterServiceInfo
    run DiameterPerLegInfo mode "outbound"
}

SipAccess_SubscriberCheck:sipcall:rest_demo:

featurescript RestDemoSessionCheck {
    if not session.PlayAnnouncementThenEndSession {
        run SipSubscriberDetermination
        run CallPartiesAddressDetermination
        run SessionTracing
    }
}

SipAccess_PartyResponse:sipcall:rest_demo:

featurescript RestDemoSubscriberCheck {
    if not session.PlayAnnouncementThenEndSession {
        run SubscriberDataLookup
        run SubscriberValidity
    }
}

SipAccess_PartyRequest:sipcall:rest_demo:

featurescript RestSipAccessPartyRequest {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipAccess_PartyResponse:sipcall:rest_demo:

featurescript RestSipAccessPartyResponse {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipMidSession_PartyRequest:sipcall:rest_demo:

featurescript DemoMidSessionPartyRequest {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}

SipMidSession_PartyResponse:sipcall:rest_demo:

featurescript DemoMidSessionPartyResponse {
    run RestSendCallNotification
    if session.PlayAnnouncementThenEndSession {
        run SipPlayAnnouncement
    }
}
Previous page Next page