Monday, 27 June 2022

How to use the ABAP Client Proxy to consume a public REST service.

This blog shows how the Client Proxy (CP) can be used to consume any public REST service. The features used in this blog will be available from ABAP Platform 2022 and the corresponding SP’s in SAP Note: 2512479.

For our example, the PetStore-V3 REST API from swagger has been used. Since it is based on the Open API 3.0 standard and has no authorization headers, so the setup is easy. We will use public HTTP endpoints of the service to perform typical CRUD ( Create, Retrieve, Update, Delete ) operations. Although any public REST service can be consumed via CP, it is really helpful if it is based on Open API 3.0 because then the field mapping from JSON to ABAP is easy to infer.

Analyzing the Service

Before we can consume the service we should perform the following analytical tasks to better understand the service we are going to consume and also the corresponding CP coding:

◉ Identifying Entities

◉ Identifying Endpoints

In our case, we do this via postman but you can use any method of preference.

Please note that this step is entirely optional but highly recommended, if you want to skip this section go directly to “Consuming Service via CP” heading

Importing the service definition in Postman

Download the Open API 3.0 definition for the service and do the following:

1. Go to Workspaces->My Workspace (Or any other in your Postman).

2. Click on APIs.

3. Click on Import button and open the JSON file downloaded above.

4. A pop will appear as shown below, confirm that the information for Name and Format is as shown here and then click the Import button.

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP News, SAP ABAP Process, SAP ABAP Preparation Exam, SAP ABAP Tutorial and Material
The API definition should now be showing in your postman.

Identifying Entities

◉ Expand the newly added entry “Swagger Petstore – OpenAPI 3.0″ in the navigation sub-tree on the left hand-side, then click on draft and then the Definition Tab as highlighted below.
◉ Here you can see all components, paths and other information for the service. For our purpose, we are going to consume the Pet component only.
◉ Take note of the JSON structure of this object and get an understanding for all fields names, types and nesting. For example: the Tag and Category components are contained within the Pet.

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP News, SAP ABAP Process, SAP ABAP Preparation Exam, SAP ABAP Tutorial and Material
Pet Component as defined in the API

Identifying Endpoints

To view and call all the API HTTP endpoints, first set the environment variable correctly.

1. Click on draft -> Swagger Petstore – OpenAPI -> Variables and set the baseURL to https://petstore3.swagger.io/api/v3.

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP News, SAP ABAP Process, SAP ABAP Preparation Exam, SAP ABAP Tutorial and Material

2. Navigate to Collections in left-most Navigation pane and click on the PetStore collection. All API endpoints should now be working.

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP News, SAP ABAP Process, SAP ABAP Preparation Exam, SAP ABAP Tutorial and Material

For this example we will use the highlighted requests only, take a note of these as we will refer to them often throughout the tutorial. They have been listed here again for convenience:

1. Find pet by id
2. Update a pet in the store
3. Update an existing Pet
4. Add a new pet to pet store
5. Delete a pet

Now that we have analyzed the service and decided what we want to consume, we can start our work in ABAP!

Consuming Service via CP


Following classes must be defined for consuming the service:

1. The interface for structure types and constants

2. The client proxy model

3. The client to consume the service

Each of these is described in detail in the sections below. Just copy the code snippets in order and by the end you will have all three classes. All blocks of code have been explained thoroughly.

Note: when copying the code in this blog replace the suffix ZAF_ with your own one

Interface for structure types and constants

Create a new interface and give it a suitable name e.g. “zaf_if_petstore_types“.

To facilitate our other classes and for cleanliness, we will group together constants and structure types in this interface. Copy the ABAP code below in your interface class.

Structure types

The PET structure with all its underlying entities and fields has to be defined as shown. Since it contains the TAG and CATEGORY entities they have to be defined before.

ABAP definition

  TYPES:

    BEGIN OF tys_category,
      id   TYPE i,
      name TYPE string,
    END OF tys_category,

    BEGIN OF tys_tag,
      id   TYPE i,
      name TYPE string,
    END OF tys_tag,

    BEGIN OF tys_pet,
      id         TYPE i,
      name       TYPE string,
      category   TYPE tys_category,
      photo_urls TYPE STANDARD TABLE OF string    WITH DEFAULT KEY,
      tags       TYPE STANDARD TABLE OF tys_tag  WITH DEFAULT KEY,
      status     TYPE string,
    END OF tys_pet.

The definition is based on the JSON structure of the entity. Looking at the XML response of a get request, we get an idea of teh structure. A more precise way is to check the Open API 3.0 schema definition via Postman

XML response of a get request (DO NOT COPY!)

<Pet>
    <category>
        <id>0</id>
        <name>Pug</name>
    </category>
    <id>3</id>
    <name>doggie</name>
    <photoUrls>
        <photoUrl>axyz.in</photoUrl>
        <photoUrl>axyz.in</photoUrl>
        <photoUrl>axyz.in</photoUrl>
    </photoUrls>
    <status>available</status>
    <tags>
        <tag>
            <id>22</id>
            <name>dog</name>
        </tag>
    </tags>
</Pet>

Constants

Next we define resource names and paths for each endpoint that we want to consume. So for all five endpoints identified previously, we have to define a resource path/name.

For instance:
To call the API endpoint: /pet/4
the resource path /pet{id} has to be defined, where {id} is the placeholder for id parameter.
Similarly, we do this for all the other endpoints as well.

Therefore the following constants have to be defined:

◉ gcs_resourcse_paths, which contains the entire path with placeholders for parameters.
◉ gcs_resource_names is simply a name we use internally, you can choose whatever you want

Copy and paste this code in your interface class to define the constants

    BEGIN OF gcs_resource_names,
      pets              TYPE /iwbep/if_v4_rest_types=>ty_internal_name  VALUE `PETS`,
      pet_id             TYPE /iwbep/if_v4_rest_types=>ty_internal_name  VALUE `PET_ID`,
      pet_id_name_status TYPE /iwbep/if_v4_rest_types=>ty_internal_name  VALUE `PET_ID_NAME_STATUS`,
    END OF  gcs_resource_names,

    BEGIN OF gcs_resource_paths,
      pets               TYPE string  VALUE `/pet`,
      pet_id             TYPE string  VALUE `/pet/{id}`,
      pet_id_name_status TYPE string  VALUE `/pet/{id}?name={name}&status={status}`,
    END OF  gcs_resource_paths.

Now the interface class is complete, next the Client Proxy Model has to be defined.

Defining the Client Proxy Model.

The Client Proxy Model performs these tasks:

◉ Defines the structure for entities to be consumed using ABAP structures
◉ Defines resources for each http endpoint.
◉ Define one or more operations on each resource.

Create a new class e.g. zaf_cl_rest_petstore_model and copy paste the code snippets in this section one-by-one, in order.

Class Definition

◉ The public method /iwbep/if_v4_mp_basic_rest~define is an interface method which must be implemented by every consumption model class.

◉ define_pet defines our resource to be consumed and takes as input the model object

◉ All models must inherit the /iwbep/cl_v4_abs_model_prov base class.

CLASS zaf_cl_rest_petstore_model DEFINITION
  PUBLIC
  INHERITING FROM /iwbep/cl_v4_abs_model_prov
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS /iwbep/if_v4_mp_basic_rest~define REDEFINITION.

  PRIVATE SECTION.

    "! <p class="shorttext synchronized" lang="en">Define the structure type and resources for Pet</p>
    "!
    "! @parameter io_model        | <p class="shorttext synchronized" lang="en">Proxy model</p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en">Gateway exception</p>
    METHODS define_pet
      IMPORTING
        io_model TYPE REF TO /iwbep/if_v4_rest_model
      RAISING
        /iwbep/cx_gateway .

ENDCLASS.​

Class Implementation

Both methods are implemented using the code in snippets below. Just copy and paste the code snippets and everything should work. Important points are explained for each snippet

Method /iwbep/if_v4_mp_basic_rest~define .

This method just calls the inner method and passes the io_model parameter.

CLASS zaf_cl_rest_petstore_model IMPLEMENTATION.

  METHOD  /iwbep/if_v4_mp_basic_rest~define.
    define_pet( CAST /iwbep/if_v4_rest_model( io_model ) ).
  ENDMETHOD.

Method define_pet.

The main method where we define the structure types, resources and operations that can be performed on the service.

Data Declaration

◉ Each of the three entities to be consumed need an ABAP structure for structure definition.
 
 METHOD define_pet.
    DATA:
      ls_data_container_pet      TYPE zaf_if_petstore_types=>tys_pet,
      ls_data_container_tag      TYPE zaf_if_petstore_types=>tys_tag,
      ls_data_container_category TYPE zaf_if_petstore_types=>tys_category,
      lo_structured_type         TYPE REF TO /iwbep/if_v4_rest_struc_type,
      lo_resource                TYPE REF TO /iwbep/if_v4_rest_resource,
      lo_operation               TYPE REF TO /iwbep/if_v4_rest_operation.
Defining Structures

◉ The nested structure types Tag and Category must be defined first, followed by Pet

◉ All primitive property names must be lowercase to match the JSON field names.

◉ Since the Pet structure contains Tag and Category, they must be added as properties to the Pet Type

*Define Category
    lo_structured_type = io_model->create_struct_type_by_struct(
                                      iv_name              = zaf_if_petstore_types=>gcs_internal_struct_type_names-category
                                      is_structure         = ls_data_container_category
                                      iv_do_gen_prim_props = abap_true ).

    lo_structured_type->camelcase_lower_prim_prp_names(  ).

*Define Tag
    lo_structured_type = io_model->create_struct_type_by_struct(
                                            iv_name              = zaf_if_petstore_types=>gcs_internal_struct_type_names-tag
                                            is_structure         = ls_data_container_tag
                                            iv_do_gen_prim_props = abap_true ).

    lo_structured_type->camelcase_lower_prim_prp_names(  ).

*Define Pet
    lo_structured_type = io_model->create_struct_type_by_struct(
                                            iv_name                   = zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
                                            is_structure              = ls_data_container_pet
                                            iv_do_gen_prim_props      = abap_true
                                            iv_do_gen_prim_prop_colls = abap_true ).

    lo_structured_type->camelcase_lower_prim_prp_names(  ).

    lo_structured_type->create_structured_property( 'CATEGORY'
                     )->set_external_name( 'category'
                     )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-category ).

    lo_structured_type->create_structured_property( 'TAGS'
                     )->set_external_name( 'tags'
                     )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-tag )->set_is_collection(  ).
 
Defining Resources and Operations

◉ Resources are defined for each unique http endpoint/path.

◉ The import parameters for the method create_resource are filled using the corresponding constants defined in the interface.

◉ For parameters inside the path template, a structure type must also be defined using set_path_params_struct_type.Example
Given this path template: /pet/{id}
the structured type must contain one primitive property (e.g. with the name ID), that has the external name id, in our case this is simply the pet structure type.

◉ Operations are defined on a resource using an HTTP verb, and can further have a request/response body

◉ The response body and the request body can also be provided a structure type depending on the type of operation being performed.Example
GET /pet/{id} has a response body with type PET but no request body
POST /pet has a response and request body with type PET.

◉ For certain unique operations such as delete in this case, an operation may not have a structure type for a request body and response body both.

◉ For delete, the response is simple JSON “pet_deleted”, which doesn’t correspond to any type.
  
  lo_resource = io_model->create_resource( iv_name          = zaf_if_petstore_types=>gcs_resource_names-pet_id
                                             iv_path_template = zaf_if_petstore_types=>gcs_resource_paths-pet_id ).

    lo_resource->set_path_params_struct_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
              )->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-get
              )->create_response_body(
              )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

*Delete
    lo_resource->set_path_params_struct_type(  zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
              )->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-delete
              )->create_response_body( ).

*Update Status and Name
    io_model->create_resource( iv_name          = zaf_if_petstore_types=>gcs_resource_names-pet_id_name_status
                               iv_path_template = zaf_if_petstore_types=>gcs_resource_paths-pet_id_name_status
                         )->set_path_params_struct_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
                         )->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-post
                         )->create_response_body(
                         )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

    lo_resource = io_model->create_resource( iv_name           = zaf_if_petstore_types=>gcs_resource_names-pets
                                             iv_path_template  = zaf_if_petstore_types=>gcs_resource_paths-pets ).

*Create
    lo_operation = lo_resource->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-post ).
    lo_operation->create_request_body(
               )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).
    lo_operation->create_response_body(
               )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

*Update with put
    lo_operation = lo_resource->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-put ).
    lo_operation->create_request_body(
               )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).
    lo_operation->create_response_body(
               )->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

 END METHOD.
END CLASS.

This concludes our model class, next we will define the client which will be used to execute the model operations.

Defining the client to consume the service

Create a new class in the same package as the model and give it a suitable name e.g. zaf_cl_rest_petstore_client.

Just like in the previous section, keep copying the code snippets in order of appearance. Each snippet is accompanied by a description of main concepts.

Class Definition.

The class has overall five methods, each corresponding to an operation on the API. In the Analysis section we identified five operations that we want to perform using CP, you can check them out again for reference.

For each of these operations, we will define a method, there’s also method for instantiating the client proxy object. The code snippet below defines all these methods for you. The comments also explain the purpose in detail.

CLASS zaf_cl_rest_petstore_client DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS CONSTRUCTOR raising /iwbep/cx_gateway.

    "! <p class="shorttext synchronized" lang="en">Create a Pet</p>
    "! @parameter is_pet | <p class="shorttext synchronized" lang="en">typed structure containing create data</p>
    "! @parameter rs_pet | <p class="shorttext synchronized" lang="en">typed structure containing response data from server </p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
    METHODS pet_create
      IMPORTING
        is_pet        TYPE zaf_if_petstore_types=>tys_pet
      RETURNING
        VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet
      RAISING
        /iwbep/cx_gateway.

    "! <p class="shorttext synchronized" lang="en">Delete a Pet with supplied id</p>
    "! This method does not return a json response body which can be serialized in to abap structure, so the http code is returned.
    "! @parameter iv_id | <p class="shorttext synchronized" lang="en"></p>
    "! @parameter rv_http_status_code | <p class="shorttext synchronized" lang="en"></p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
    METHODS pet_delete
      IMPORTING
        iv_id                      TYPE  i
      RETURNING
        VALUE(rv_http_status_code) TYPE i
      RAISING
        /iwbep/cx_gateway.

    "! <p class="shorttext synchronized" lang="en">Get a pet with supplied id</p>
    "!
    "! @parameter iv_id | <p class="shorttext synchronized" lang="en">The id of the pet</p>
    "! @parameter rs_pet | <p class="shorttext synchronized" lang="en">Returned pet as ABAP structure, complete with deep fields.</p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
    METHODS pet_read_by_id
      IMPORTING
        iv_id         TYPE  i
      RETURNING
        VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet
      RAISING
        /iwbep/cx_gateway.

    "! <p class="shorttext synchronized" lang="en">Update a pet using PUT</p>
    "! @parameter is_pet | <p class="shorttext synchronized" lang="en">typed structure containing update data</p>
    "! @parameter rs_pet | <p class="shorttext synchronized" lang="en">typed structure containing response data from server </p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
    METHODS pet_update
      IMPORTING
        is_pet        TYPE zaf_if_petstore_types=>tys_pet
      RETURNING
        VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet
      RAISING
        /iwbep/cx_gateway.

    "! <p class="shorttext synchronized" lang="en">Update the name and status of the pet having the supplied ID</p>
    "! The variables to be updated are sent as query parameters. The request type is POST
    "! @parameter iv_id | <p class="shorttext synchronized" lang="en">ID of the pet that is to be updated.</p>
    "! @parameter iv_name | <p class="shorttext synchronized" lang="en">new name for the pet</p>
    "! @parameter iv_status | <p class="shorttext synchronized" lang="en">new status for the pet</p>
    "! @parameter rs_pet | <p class="shorttext synchronized" lang="en">Updated pet </p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
    METHODS pet_update_name_and_status
      IMPORTING
        iv_id         TYPE  i
        iv_name       TYPE  zaf_if_petstore_types=>tys_pet-name
        iv_status     TYPE  zaf_if_petstore_types=>tys_pet-status
      RETURNING
        VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet
      RAISING
        /iwbep/cx_gateway.

PRIVATE SECTION.

  DATA: mo_client_proxy  TYPE REF TO /iwbep/if_cp_client_proxy_rest,
        mo_http_client   TYPE REF TO if_http_client.

  METHODS create_client_proxy
    RETURNING
      VALUE(ro_client_proxy) TYPE REF TO /iwbep/if_cp_client_proxy_rest
    RAISING
      /iwbep/cx_gateway.

ENDCLASS.

Class Implementation

The implementation of each method is very similar. Using method chaining, we add the necessary information to carry out each request and also provide the payload data if required. For some methods we also receive the response data typed as PET object. The code in these sections should be self-explanatory, in case of questions just write a comment.

First add the implementation class header, followed by all the method definitions.

CLASS zaf_cl_rest_petstore_client IMPLEMENTATION.
 
Methods

Constructor

  METHOD constructor.
   mo_client_proxy = create_client_proxy(  ).
  ENDMETHOD.

Pet_create

 METHOD pet_create.
*https://petstore3.swagger.io/api/v3/pet POST
    mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pets
                  )->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-post
                  )->set_body_data( is_pet
                  )->execute(
                  )->get_body_data( IMPORTING ea_body_data = rs_pet ).

  ENDMETHOD.
 
Pet_delete

  METHOD pet_delete.
*https://petstore3.swagger.io/api/v3/pet DELETE
    DATA ls_key TYPE zaf_if_petstore_types=>tys_pet.

    rv_http_status_code = mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names pet_id
                                        )->set_path_template_parameters( ls_key
                                        )->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-delete
                                        )->execute( )->get_http_status_code(  ).
  ENDMETHOD.

Pet_read_by_id

  METHOD pet_read_by_id.
*https://petstore3.swagger.io/api/v3/pet/{id} GET
    mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pet_id
                  )->set_path_template_parameters(  VALUE zaf_if_petstore_types=>tys_pet( id  = iv_id )
                  )->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-get
                  )->execute(
                  )->get_body_data( IMPORTING ea_body_data = rs_pet ).
  ENDMETHOD.
 
Pet_update

 METHOD pet_update.
*https://petstore3.swagger.io/api/v3/pet PUT
    mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pets
                  )->create_request(  /iwbep/if_v4_rest_types=>gcs_http_method-put
                  )->set_body_data( is_pet
                  )->execute(
                  )->get_body_data( IMPORTING ea_body_data = rs_pet ).
  ENDMETHOD.

Pet_update_name_and_status

 METHOD pet_update_name_and_status.
*https://petstore3.swagger.io/api/v3/pet/{id}?name={name}&status={status} POST
    mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pet_id_name_status
                  )->set_path_template_parameters(
                       VALUE zaf_if_petstore_types=>tys_pet(
                                id     = iv_id
                                name   = iv_name
                                status = iv_status )
                  )->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-post
                  )->execute(
                  )->get_body_data( IMPORTING ea_body_data = rs_pet ).
  ENDMETHOD.

Create_client_proxy

 METHOD create_client_proxy.

    DATA:

      ls_proxy_model_key TYPE /iwbep/if_cp_registry_types=>ty_s_proxy_model_key,
      lv_error_text      TYPE string.

    cl_http_client=>create_by_url(
      EXPORTING
        url                    = 'https://petstore3.swagger.io'
      IMPORTING
        client                 = mo_http_client
      EXCEPTIONS
        argument_not_found     = 1
        plugin_not_active      = 2
        internal_error         = 3
        pse_not_found          = 4
        pse_not_distrib        = 5
        pse_errors             = 6
        OTHERS                 = 7 ).

    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_error_text.
      RAISE EXCEPTION TYPE lx_rest_json_pl
        EXPORTING
          iv_error_text = lv_error_text.
    ENDIF.

    "Proxy model defined in ZAF_CL_REST_PETSTORE_MODEL
    ls_proxy_model_key = VALUE #( repository_id       = /iwbep/if_cp_registry_types=>gcs_repository_id-default
                                  proxy_model_id      = 'ZAF_REST_PETSTORE'
                                  proxy_model_version = 0003 ).

    ro_client_proxy ?= /iwbep/cl_cp_client_proxy=>create_rest_remote_proxy(
                is_proxy_model_key       = ls_proxy_model_key
                io_http_client           = mo_http_client
                iv_do_fetch_csrf_token   = abap_false
                iv_relative_service_root = '/api/v3').

  ENDMETHOD.

Now that we have created both the Model and Client, we have to register the client proxy and setup the SSL certificate for the remote service.

Register Client Proxy

Before we can use our proxy model, it must be registered. Perform the following steps to register it:

1. Open the Transaction /IWBEP/CP_ADMIN
2. Click on Create and enter the details as shown below

Proxy Model ID *You can choose any value e.g ZAF_REST_PETSTORE
Version 
Model Provider Class   *Enter name of your proxy model class 
Description  *Enter any description 
Package  $TMP 

After successfully completing these steps your proxy model should appear in the list of models in the transaction.

Adding SSL certificate for the remote service

The SSL certificate for the remote service  https://petstore3.swagger.io/ has to be added in the ABAP system before it can be accessed. Refer to this blog post on how to do that.

Executing the Client Class Methods

Congratulations if you have come this far! Now the last step is to use our Client and Proxy Model classes to create unit tests which actually consume the service. The unit test class has one method which performs all CRUD operations and then compares actual vs expected data for each operation.

Copy this code in the unit class:

CLASS zaf_cl_petstore_test DEFINITION DEFERRED.
class zaf_cl_rest_petstore_client  definition local friends
zaf_cl_petstore_test.

CLASS zaf_cl_petstore_test DEFINITION
FINAL
FOR TESTING
DURATION SHORT
  RISK LEVEL HARMLESS.

  PUBLIC SECTION.

    METHODS:
      pet_operations FOR TESTING RAISING /iwbep/cx_gateway.

  PRIVATE SECTION.

    DATA mo_cut TYPE REF TO zaf_cl_rest_petstore_client.

    METHODS: setup RAISING /iwbep/cx_gateway.

ENDCLASS.

CLASS zaf_cl_petstore_test IMPLEMENTATION.

  METHOD pet_operations.

    DATA:
      ls_payload           TYPE zaf_if_petstore_types=>tys_pet,
      ls_response_exp     TYPE zaf_if_petstore_types=>tys_pet,
      ls_response_act     TYPE zaf_if_petstore_types=>tys_pet,
      ls_category         TYPE zaf_if_petstore_types=>tys_category,
      lv_http_status_code TYPE string,
      lv_id               TYPE i,
      lx_gateway          TYPE REF TO /iwbep/cx_gateway.

*CREATE
    ls_category = VALUE #(
                       id   = 20
                       name = 'Persian'  ).
    ls_payload = VALUE #(
              id         = 34
              name       = 'Cat'
              status     = 'Brand New'
              category   = ls_category
              photo_urls = VALUE #( ( |url_1| )  ) ).

    ls_response_exp = ls_payload.

    ls_response_act = mo_cut->pet_create( ls_payload ).

    "Then the expected pet data should be returned.
    cl_abap_unit_assert=>assert_equals( exp =  ls_response_exp
                                        act =  ls_response_act ).
    CLEAR ls_response_act.

*READ BY ID
    "When we make a get request for a pet with this id.
    lv_id = 34.
    ls_response_act = mo_cut->pet_read_by_id( lv_id ).

    "Then the expected pet data should be returned.
    cl_abap_unit_assert=>assert_equals( exp =  ls_response_exp
                                        act =  ls_response_act ).
    CLEAR ls_response_act.

*UPDATE NAME AND STATUS
    ls_response_act = mo_cut->pet_update_name_and_status(
                    iv_id     =  34
                    iv_name   = 'new_name'
                    iv_status = 'new_status' ).

    ls_response_exp-name    = 'new_name'.
    ls_response_exp-status  = 'new_status'.

    cl_abap_unit_assert=>assert_equals( exp =  ls_response_exp
                                        act =  ls_response_act ).

*UPDATE
    ls_payload-name = 'Schrodingers Cat'.
    ls_payload-status = 'Dead and Alive'.
    ls_payload-category = VALUE #(
                       id   = 21
                      name = 'Mystery Kitty'  ).

    ls_response_exp = ls_payload.

    ls_response_act = mo_cut->pet_update( ls_payload ).

    cl_abap_unit_assert=>assert_equals( exp =  ls_response_exp
                                        act =  ls_response_act ).

*DELETE
  lv_http_status_code = mo_cut->pet_delete( 4 ).

 "the expected pet data should be returned.
    cl_abap_unit_assert=>assert_equals( exp =  200
                                        act =  lv_http_status_code ).

  ENDMETHOD.

  METHOD setup.
   mo_cut = NEW zaf_cl_rest_petstore_client( ).
  ENDMETHOD.

ENDCLASS.
 
Once everything is set-up you can execute the unit tests and they should be green! Now pat yourself on the back and enjoy some coffee

SAP ABAP, SAP ABAP Exam, SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP News, SAP ABAP Process, SAP ABAP Preparation Exam, SAP ABAP Tutorial and Material

Source: sap.com

No comments:

Post a Comment