Sunday, March 25, 2018

ODATA Limitations - Actions, Functions



http://www.odata.org/blog/actions-in-odata/

Today in OData the only way to achieve something similar would be to model Actions as Entities, but that is a low fidelity experience with additional baggage. “Actions” will provide a way to inject behaviors into an otherwise data centric model without confusing the data aspects of the model, while still staying true to the resource oriented underpinnings of OData.


UnBound Functions

The function below returns the nearest airport with the input geography point.
GET serviceRoot/GetNearestAirport(lat = 33, lon = -118)

Bound Functions.
The request below returns the favorite airline of a person, in TripPin service, "favorite airline" means airline which user choose most times. The function GetFavoriteAirline() is bound to Person.
GET serviceRoot/People('russellwhyte')/Microsoft.OData.SampleService.Models.TripPin.GetFavoriteAirline()

SAP ODATA has no support for this. You can only create Bound Functions

https://help.sap.com/viewer/68bf513362174d54b58cddec28794093/7.4.19/en-US/c5dc22512c312314e10000000a44176d.html

Gateway Debug Points- Navigation



/IWCOR/CL_DS_URI=>HANDLE_NAV_PATH_OPTIONS

 DATA:
      lv_segment TYPE string,
      lv_property TYPE string,
      lv_parentheses TYPE string,
      lv_length TYPE i,
      lv_length_minus_2 TYPE i,
      lv_key_predicate TYPE string,
      lo_from_entity_type TYPE REF TO /iwcor/if_ds_edm_entity_type,
      lo_property TYPE REF TO /iwcor/if_ds_edm_typed,
      lo_property_type TYPE REF TO /iwcor/if_ds_edm_type,
      lo_entity_property TYPE REF TO /iwcor/if_ds_edm_property,
      lo_navigation_property TYPE REF TO /iwcor/if_ds_edm_nav_property,
      lr_navigation_end TYPE REF TO /iwcor/if_ds_uri=>navigation_path_segment_s,
      lv_lines TYPE i.

    lo_from_entity_type io_from_entity_set->get_entity_type).

    lv_segment shift).

    " Case 0: Segment is $count
    IF lv_segment '$count'.
      mo_target_type lo_from_entity_type"Target Type for URI 15, 16
      mo_target_entity_set io_from_entity_set.
      IF mt_segment IS INITIAL.
        /iwcor/if_ds_uri~count abap_true.
        IF /iwcor/if_ds_uri~navigation_path IS INITIAL.
          IF /iwcor/if_ds_uri~key_predicates IS INITIAL.
            /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_15.
          ELSE.
            /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_16.
          ENDIF.
        ELSE.
          " $count of last navigation property in navigation path
          lv_lines lines/iwcor/if_ds_uri~navigation_path ).
          READ TABLE /iwcor/if_ds_uri~navigation_path INDEX lv_lines REFERENCE INTO lr_navigation_end.
          IF lr_navigation_end->key_predicates IS INITIAL AND
             lr_navigation_end->navigation_property->get_multiplicity/iwcor/if_ds_edm=>gc_multiplicity_many.
            /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_15.
          ELSE.
            /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_16.
          ENDIF.
        ENDIF.
      ELSE.
        " $count must be the last segment.
        RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_syntax_error
          EXPORTING
            textid  /iwcor/cx_ds_uri_syntax_error=>segment_not_last_segment
            segment lv_segment.
      ENDIF.
      RETURN.
    ENDIF.

    " Case 1: Segment is $Value - Media Resource only valid, if entity type has annotation "hasStream=true"
    IF lv_segment '$value'.

      IF lo_from_entity_type->has_streamabap_true.
        lv_segment shift).
        IF lv_segment IS NOT INITIAL.
          RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_syntax_error
            EXPORTING
              textid  /iwcor/cx_ds_uri_syntax_error=>segment_last_segment
              segment '$value'.
        ENDIF.
        /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_17.
        mo_target_type lo_from_entity_type"Target Type for URI 17
        mo_target_entity_set io_from_entity_set.
        /iwcor/if_ds_uri~value abap_true.
        RETURN.
      ELSE.
        RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_syntax_error
          EXPORTING
            textid  /iwcor/cx_ds_uri_syntax_error=>invalid_media_resource_request
            segment lv_segment.
      ENDIF.
    ENDIF.

    " Case 2: Segment is $links - followed by a Navigation Property.
    IF lv_segment '$links'.
      /iwcor/if_ds_uri~links abap_true.
      handle_nav_propertiesio_from_entity_set io_from_entity_set ).
      RETURN.
    ENDIF.

    " The multiplicity of of a navigation property has to be evaluated to prevent URIs like
    " ...Employees('1')/ne_Team()/nt_Employees where ne_Team is an entity type instance,
    " thus parentheses are not allowed after ne_Team.

    " Case 3: Segment is a simple, complex or a Navigation Property.
    FIND REGEX '^([^()]+)(\(.*\))?$' IN lv_segment SUBMATCHES lv_property lv_parentheses.
    IF sy-subrc IS NOT INITIAL.
      RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_not_matching
        EXPORTING
          textid  /iwcor/cx_ds_uri_not_matching=>invalid_segment
          segment lv_segment.
    ENDIF.
    lv_length strlenlv_parentheses ).

    lo_property lo_from_entity_type->get_propertylv_property ).
    IF lo_property IS NOT BOUND.
      RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_not_matching
        EXPORTING
          textid  /iwcor/cx_ds_uri_not_matching=>invalid_segment
          segment lv_segment.
    ENDIF.

    lo_property_type lo_property->get_type).
    CASE lo_property_type->kind.
      WHEN /iwcor/if_ds_edm_type=>kind_simple OR /iwcor/if_ds_edm_type=>kind_complex.
        IF lv_length IS NOT INITIAL.
          RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_not_matching
            EXPORTING
              textid  /iwcor/cx_ds_uri_not_matching=>invalid_segment
              segment lv_segment.
        ENDIF.
        lo_entity_property ?= lo_property.
        handle_property_pathlo_entity_property ).
      WHEN /iwcor/if_ds_edm_type=>kind_navigation.
        lo_navigation_property ?= lo_property.
        IF lv_length > 2.
          lv_length_minus_2 lv_length 2.
          lv_key_predicate lv_parentheses+1(lv_length_minus_2).
        ENDIF.
        IF lv_parentheses '()' AND lo_navigation_property->get_multiplicity<> /iwcor/if_ds_edm=>gc_multiplicity_many.
          RAISE EXCEPTION TYPE /iwcor/cx_ds_uri_not_matching
            EXPORTING
              textid  /iwcor/cx_ds_uri_not_matching=>invalid_segment
              segment lv_segment.
        ENDIF.
        handle_nav_propertiesio_from_entity_set io_from_entity_set io_navigation_property lo_navigation_property iv_key_predicate lv_key_predicate ).
    ENDCASE.


Gateway Debug Points lo_uri io_uri, key predicates


Class:   /IWCOR/CL_DS_HDLR_ROOT
Method: DISPATCH

  TRY.
      lv_resource_path io_context->get_resource_path).
      lt_parameter io_request->get_uri_query_parametersiv_encoded abap_false ).
      lo_uri /iwcor/cl_ds_uri_facade=>parse_uri(
        io_edm lo_edm
        iv_resource_path lv_resource_path
        it_query_parameter lt_parameter
      ).
    CATCH /iwcor/cx_ds_edm_error INTO lo_edm_error.
      RAISE EXCEPTION TYPE /iwcor/cx_ds_internal_error
        EXPORTING
          previous lo_edm_error.
    CLEANUP.
      IF lo_uri IS BOUND.
        io_context->set_objectiv_name /iwcor/cl_ds_cntxt=>gc_object_uri io_object lo_uri ).
      ENDIF.
  ENDTRY.



      /iwcor/if_ds_uri~key_predicates parse_keyiv_key_predicate iv_key_predicate io_entity_type lo_entity_type ).

      IF mt_segment IS NOT INITIAL.
        handle_nav_path_optionsio_from_entity_set io_entity_set ).
      ELSE"URI Endpoint Entity
        /iwcor/if_ds_uri~uri_type /iwcor/if_ds_uri~gc_uri_type_2.
      ENDIF.



Gateway Debug Points


Debug point to decide Entity, Entityset, Count, Metadata etc.

Class: /IWCOR/CL_DS_PROC_DISPATCHER
method /IWCOR/IF_DS_PROCESSOR~PROCESS.
  DATA:
    lo_uri TYPE REF TO /IWCOR/if_DS_uri,
    lv_http_method TYPE string,
    lv_content_type TYPE string,
    lo_edm_error TYPE REF TO /IWCOR/cx_DS_edm_error,
    lo_move_cast_error TYPE REF TO cx_sy_move_cast_error,
    lo_multipart_entity TYPE REF TO /IWCOR/if_REST_MP_entity,
    lo_entity TYPE REF TO /iwcor/if_rest_entity,
    lo_ds_exception TYPE REF TO /iwcor/cx_ds_exception.

  mo_context io_context.
  TRY.
      mo_service ?= io_context->get_service).
    CATCH cx_sy_move_cast_error INTO lo_move_cast_error.
      RAISE EXCEPTION TYPE /IWCOR/cx_DS_internal_error
        EXPORTING
          previous lo_move_cast_error.
  ENDTRY.

  mv_header_accept io_request->get_header_fieldif_http_header_fields=>accept ).

  lv_http_method io_request->get_method).
  TRANSLATE lv_http_method TO UPPER CASE.
  lo_uri io_context->get_uri).

  TRY.

    CASE lo_uri->uri_type.

        " service
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_0.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_serviceio_uri lo_uri io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity set
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_1 OR
           /IWCOR/if_DS_uri=>gc_uri_type_6b.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_entity_set(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_post.
            lo_entity io_request->get_entity).
            process_entity_set(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_2 OR
           /IWCOR/if_DS_uri=>gc_uri_type_6a.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get OR
               /IWCOR/if_REST_request=>gc_method_delete.
            process_entity(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_put OR
               /IWCOR/if_REST_request=>gc_method_merge OR
               /IWCOR/if_REST_request=>gc_method_patch.
            lo_entity io_request->get_entity).
            process_entity(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity complex property
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_3.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_complex_property(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_put OR
               /IWCOR/if_REST_request=>gc_method_merge OR
               /IWCOR/if_REST_request=>gc_method_patch.
            lo_entity io_request->get_entity).
            process_complex_property(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity simple property or entity simple property value
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_4 OR
           /IWCOR/if_DS_uri=>gc_uri_type_5.
        IF lo_uri->value abap_true.
          CASE lv_http_method.
            WHEN /IWCOR/if_REST_request=>gc_method_get OR
                 /IWCOR/if_REST_request=>gc_method_delete.
              process_property_value(
                iv_http_method lv_http_method
                io_uri lo_uri
                io_response io_response ).
            WHEN /IWCOR/if_REST_request=>gc_method_put OR
                 /IWCOR/if_REST_request=>gc_method_merge OR
                 /IWCOR/if_REST_request=>gc_method_patch.
              lo_entity io_request->get_entity).
              process_property_value(
                iv_http_method lv_http_method
                io_uri lo_uri
                io_request_entity lo_entity
                io_response io_response ).
            WHEN OTHERS.
              RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
          ENDCASE.
        ELSE.
          CASE lv_http_method.
            WHEN /IWCOR/if_REST_request=>gc_method_get.
              process_simple_property(
                iv_http_method lv_http_method
                io_uri lo_uri
                io_response io_response ).
            WHEN /IWCOR/if_REST_request=>gc_method_put OR
                 /IWCOR/if_REST_request=>gc_method_merge OR
                 /IWCOR/if_REST_request=>gc_method_patch.
              lo_entity io_request->get_entity).
              process_simple_property(
                iv_http_method lv_http_method
                io_uri lo_uri
                io_request_entity lo_entity
                io_response io_response ).
            WHEN OTHERS.
              RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
          ENDCASE.
        ENDIF.

        " entity link
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_7a.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get OR
               /IWCOR/if_REST_request=>gc_method_delete.
            process_link(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_put OR
               /IWCOR/if_REST_request=>gc_method_merge OR
               /IWCOR/if_REST_request=>gc_method_patch.
            lo_entity io_request->get_entity).
            process_link(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity links
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_7b.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_links(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_post.
            lo_entity io_request->get_entity).
            process_links(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        "  $metadata
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_8.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_metadataio_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " $batch
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_9.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_post.
            lv_content_type io_request->get_header_fieldif_http_header_fields=>content_type ).
            TRANSLATE lv_content_type TO LOWER CASE.
            IF lv_content_type IS INITIAL.
              RAISE EXCEPTION TYPE /IWCOR/cx_DS_bad_request.
            ELSEIF strlenlv_content_type 15 or lv_content_type(15<> /IWCOR/if_REST_media_type=>gc_multipart_mixed.
              RAISE EXCEPTION TYPE /IWCOR/cx_DS_unsupp_media_type.
            ENDIF.
            TRY.
                lo_multipart_entity ?= io_request->get_entity).
              CATCH cx_sy_move_cast_error INTO lo_move_cast_error.
                RAISE EXCEPTION TYPE /IWCOR/cx_DS_unsupp_media_type
                  EXPORTING
                    previous lo_move_cast_error.
            ENDTRY.
            process_batch(
              io_request_entity lo_multipart_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " function import
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_10 OR
           /IWCOR/if_DS_uri=>gc_uri_type_11 OR
           /IWCOR/if_DS_uri=>gc_uri_type_12 OR
           /IWCOR/if_DS_uri=>gc_uri_type_13.
        process_function_import(
          iv_http_method lv_http_method
          io_uri      lo_uri
          io_response io_response ).

        " function import primitve
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_14.
        IF lo_uri->value abap_true.
          process_function_import_value(
            iv_http_method lv_http_method
            io_uri      lo_uri
            io_response io_response ).
        ELSE.
          process_function_import(
            iv_http_method lv_http_method
            io_uri      lo_uri
            io_response io_response ).
        ENDIF.

        " entity set count
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_15.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_entity_set_countio_uri lo_uri io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity exists
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_16.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_entity_existsio_uri lo_uri io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " media resource
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_17.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get OR
               /IWCOR/if_REST_request=>gc_method_delete.
            process_entity_media(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_response io_response ).
          WHEN /IWCOR/if_REST_request=>gc_method_put.
            lo_entity io_request->get_entity).
            process_entity_media(
              iv_http_method lv_http_method
              io_uri lo_uri
              io_request_entity lo_entity
              io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity link exists
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_50a.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_link_existsio_uri lo_uri io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

        " entity links count
      WHEN /IWCOR/if_DS_uri=>gc_uri_type_50b.
        CASE lv_http_method.
          WHEN /IWCOR/if_REST_request=>gc_method_get.
            process_links_countio_uri lo_uri io_response io_response ).
          WHEN OTHERS.
            RAISE EXCEPTION TYPE /IWCOR/cx_DS_methd_not_allowed.
        ENDCASE.

      WHEN OTHERS.
        RAISE EXCEPTION TYPE /IWCOR/cx_DS_internal_error.
    ENDCASE.

   CATCH /iwcor/cx_ds_exception into lo_ds_exception.

      RAISE EXCEPTION TYPE /iwcor/cx_ds_internal_error
        EXPORTING
          previous lo_ds_exception.

  ENDTRY.

endmethod.