Service Interfaces

Service interfaces expose one or more operations that are used inside service model classes to define sequences of incoming and outgoing virtual service messages. You can either use a generic built-in service interface, or create a custom one by extending the built-in generic class. There is one built-in service interface class for each protocol.

REST service interface

You can use a REST service interface inside a service scenario in one of the following ways:

Defining REST service calls

When defining REST message calls using the withRequest and withResponse constructs, you must include several mandatory values, as shown below.

this.service.GET("/hello")
    .withRequest()
    .withResponse({"greeting": "Hello, world!"}, sv.JSON)
    .withStatusCode(200);
  1. For generic and Swagger-based service interfaces, you must manually provide an HTTP method with the path parameter: GET("/hello"). Auto-completion guides you in adding other HTTP methods.
  2. Provide a content type, for example the sv.JSON in .withResponse(...)
  3. Optionally, specify a status code, for example .withStatusCode(200). The default is 200 OK.
  4. If you need to work with a parametrized path, use placeholders like the {id} below. In the withPathParameters construct, the value of id parameter is saved to the selectedId SV variable.
    this.service.GET("/items/{id}")
        .withRequest()
        .withPathParameters({id: selectedId})
        ...
  5. To work with query string parameters, use the withQueryParameters construct. The example below shows the id parameter specified as a query string parameter. Never include query string parameters in the operation path (GET("/items")).
    this.service.GET("/items")
        .withRequest()
        .withQueryParameters({id: selectedId})
        ...
  6. Use the withHeaders construct to specify additional HTTP headers. Headers can be defined for both the request and response, and you can define several of them at once.
    this.service.GET("/items")
        .withRequest()
        .withHeaders({"Accept-Encoding":"gzip, deflate, br",
                      "Accept-Language":"en-US,en;q=0.9"})
        ...              
  7. To ignore headers during simulation, enclose them in an implicit SV variable:
    this.service.GET("/items")
        .withRequest()
        .withHeaders(sv.svVar({"Accept-Encoding":"gzip, deflate, br",
                               "Accept-Language":"en-US,en;q=0.9"}));
        ...                       

Using the generic REST interface

The example below uses the generic REST interface RestServiceInterface inside the service model, MyServiceModel. It passes the RestServiceInterface interface via a constructor argument to make it available inside the service model.

The VSL compiler automatically injects an instance of the service interface, whenever the service model is to be used. Subsequently you can define 'raw' REST service calls using the interface instance, as explained in Defining REST service calls.

import * as sv from "sv-vsl.js";

export class MyServiceModel extends sv.ServiceModel {

    constructor(s: sv.RestServiceInterface) {
        super(s);
        this.service = s;
    }

    @sv.scenario
    myServiceScenario() {
        this.service.GET("/items/{id}")
            .withRequest()
            .withResponse({"greeting": "Hello, world!"}, sv.JSON)
            .withStatusCode(200);
    }
}

Defining a custom REST service interface

It is a good practice to define a custom REST interface providing you with named service operations encapsulating the 'raw' HTTP calls.

To define a custom REST service interface:

  1. Create a new file and include the sv-vsl.js file from its current
    location. This is necessary in order to enable auto-completion in your IDE.
  2. Define a class that extends the _generic REST service interface, RestServiceInterface.
  3. Define operations encapsulating the 'raw' REST calls as shown below.
    Operation methods must be annotated with the @sv.operation decorator, and return the sv.RestOperation type. For example, myGetOperation(): sv .RestOperation {...}.
    import * as sv from "sv-vsl.js";
    
    export class MyServiceRestInterface extends sv.RestServiceInterface {
    
        @sv.operation
        myGetOperation(): sv.RestOperation {
            return new sv.RestOperation(sv.GET, "/status")
                .withRequest()
                .withResponse({"status":"OK"});
        }
    
        @sv.operation
        myPostOperation(): sv.RestOperation {
            return new sv.RestOperation(sv.POST, "/storage/{id}")
                .withRequest({
                    "myData": {
                         //...
                    }
                }, sv.JSON)
                .withPathParameters({id: 1})
                .withResponse({
                    "myData": {
                        //...
                    }
                }, sv.JSON);
        }
    }
  4. Import your custom service interface and make it available inside the service model via constructor argument injection, as shown below.
    import * as sv from "sv-vsl.js";
    import {MyServiceRestInterface} from "MyServiceRestInterface.js";
    
    export class MyServiceModel extends sv.ServiceModel {
    
        constructor(s: MyServiceRestInterface) {
            super(s);
            this.service = s;
        }
    
        @sv.scenario
        myServiceScenario() {
            this.service.myGetOperation()
                .withStatusCode(500) // You can further adjust
                .withDelay(500);     // the operation if needed.
        }
    }

Defining a custom REST service interface in a Swagger file

If you have an up-to-date Swagger file, you can define service interface by importing it from an external file, as shown below. Then you can use the service interface as it explicitly implemented the interface defined in the Swagger file. The service interface class must inherit from sv.RestServiceInterface. This approach is used when recording REST messages from real service using the SV Capture tool.

import * as sv from "sv-vsl.js";

export class MyServiceRestInterface extends sv.RestServiceInterface {
    constructor() {
        super();
        this.importExternal("MyServiceInterfaceSwagger.json");
    }
}

SOAP service interface

In VSL there are two base classes for SOAP service interface available. Choose Soap11ServiceInterface and use Soap11Operation for SOAP 1.1 services or Soap12ServiceInterface and use Soap12Operation for SOAP 1.2.

The SoapOperation provides the following methods:

  1. withRequest or withResponse are used to set SOAP Body content of form of XML literal or variable. I case of a response containing a SOAP Fault, use the withResponse method without any parameter.
  2. withSoapHeader specifies a SOAP header of the message. It must be either XML literal or variable. You may specify multiple SOAP headers by multiple calls of this method.
  3. SOAP Fault properties methods
  4. withFaultCode sets Fault Code property. The string parameter literal or variable specifies a local part of the QName value.
  5. withFaultDetail adds a new SOAP Fault Detail part in form of XML literal or variable. To set more detail parts call this method multiple times.
  6. withFaultString and withFaultActor set corresponding properties of SOAP 1.1 Fault response.
  7. withFaultReason and withFaultRole set corresponding properties of SOAP 1.2 Fault response.

Using the generic SOAP interface

The example below uses the generic SOAP interface Soap11ServiceInterface inside the service model, AnimalBanquetServiceModel. It passes the Soap11ServiceInterface interface via a constructor argument to make it available inside the service model.

To make a call, use the invoke method provided by the interface class. The string parameter will become a SOAP action of the operation invoked. For SOAP 1.2 service, an additional second parameter can be used to set a response SOAP action.

import * as sv from "sv-vsl.js";

export class AnimalBanquetServiceModel extends sv.ServiceModel {

    constructor(s: sv.Soap11ServiceInterface) {
        super(s);
        this.service = super.getService();
    }

    @sv.scenario
    partyScenario() {
        this.service.invoke("inviteAnimalSoapAction")
            .withRequest(
                <inviteAnimalRequest>
                    <invitation>
                        <animal>sheep</animal>
                    </invitation>
                </inviteAnimalRequest>
            )
            .withResponse(
                <inviteAnimalResponse>
                    <invitationStatus>invited</invitationStatus>
                </inviteAnimalResponse>
            );
    }
}

Defining a custom SOAP service interface

It is a good practice to define a custom SOAP interface providing you with named service operations.

import {Soap12Operation, Soap12ServiceInterface, operation} from "sv-vsl.js"

export class AnimalBanquetServiceInterface extends Soap12ServiceInterface {

    @operation
    inviteAnimal():Soap12Operation {
        return new Soap12Operation()
            .withRequest(<inviteAnimalRequest/>)
            .withResponse(<inviteAnimalResponse/>);
    }

    @operation
    rejectAnimal():Soap12Operation {
        return new Soap12Operation()
            .withRequest(
                <rejectAnimalRequest>
                    <rejection>
                        <animal>fox</animal>
                    </rejection>
                </rejectAnimalRequest>)
            .withResponse(
                <rejectAnimalResponse>
                    <invitationStatus>rejected</invitationStatus>
                </rejectAnimalResponse>);
    }
}

Import your custom service interface and make it available inside the service model via constructor argument injection, as shown below.

import * as sv from "sv-vsl.js";
import {AnimalBanquetServiceInterface} from "AnimalBanquetServiceInterface.js";

export class AnimalBanquetServiceModel extends sv.ServiceModel {
    constructor(s:AnimalBanquetServiceInterface) {
        super(s);
        this.service = super.getService();
    }

    @sv.scenario
    cancelledPartyScenario() {
        this.service.inviteAnimal()
            .withRequest(
                <inviteAnimalRequest>
                    <invitation>
                        <animal>sheep</animal>
                    </invitation>
                </inviteAnimalRequest>
            )
            .withResponse(
                <inviteAnimalResponse>
                    <invitationStatus>invited</invitationStatus>
                </inviteAnimalResponse>
            );
    }
}

Mobile API service interface

The generic Mobile API service interface provides a set of predefined operations that does not need to be customized in most cases.

The example below uses the generic MobileApiServiceInterface service interface inside the MyMobileApiServiceModel service model:

import * as sv from "sv-vsl.js";

export class MyMobileApiServiceModel extends sv.ServiceModel {
    constructor(s: sv.MobileApiServiceInterface) {
        super(s);
        this.service = s;
    }

    @sv.scenario
    myScenario() {
        this.service.nfc_processCommandApdu()
            .withRequest({
                "command":"00A4040005F222222222",
                "deactivatedArg":"-1",
                "operation":"nfc_processCommandApdu"
            })
            .withResponse({"result": "01"});
        this.service.nfc_onDeactivated()
            .withRequest({"command":"","deactivatedArg":0,
                          "operation":"nfc_onDeactivated"});
    }
}

MQTT service interface

MQTT operation definition requires payload type and topic filter parameters.

To define operations in MQTT service interface, specify the payload type and topic filter parameters.

The example below shows a customized MQTT service interface:

import * as sv from "sv-vsl.js"

export class MyMqttInterface extends sv.MqttServiceInterface {
    constructor() {
        super();
    }

    @sv.operation
    statusUpdate() : sv.MqttOperation {
        return new sv.MqttOperation("JSON", "demo/+/status");
    }
}

For details on importing and using a customized service interface, see
Defining a custom REST service interface.