Как обработать функцию импорта ODATA, не возвращая элемент оболочки - PullRequest
0 голосов
/ 05 июля 2019

В нашем решении мы вызываем пользовательский API ODATA на основе CDS на стороне S / 4HANA, который включает импорт функций.Для этого мы сгенерировали Java-сервис для интерфейса ODATA через плагин maven.Импорт вызываемой функции основан на BOPF, и сгенерированная реализация не возвращает результат, соответствующий ожиданиям SAP Cloud SDK, а именно то, что результирующий объект JSON имеет член с именем импорта функции.

Мы проверили, можем ли мы изменить сгенерированную реализацию ODATA, но не нашли конфигурации для этого в S / 4HANA.

Я выполнил отладку реализации SDK при выполнении вызова и нашел следующий код в FunctionImportResponseParser, который делаетэто предположение:

    <T> T getEntityFromResponse(
        final InputStream responseContent,
        final String edmFunctionImportName,
        final Class<? extends T> entityJavaType )
        throws IOException,
            IllegalArgumentException
    {
        final JsonObject responseJsonObject = getJsonObjectFromResponse(responseContent);

(X)     if( responseJsonObject.has(edmFunctionImportName) ) {
            final JsonElement jsonElement = responseJsonObject.get(edmFunctionImportName);
            return getEntityFromJsonElement(jsonElement, entityJavaType);
        }

        return null;
    }

Из-за оператора if, отмеченного (X), и того факта, что функция import напрямую возвращает объект, не заключая его в ожидаемый член, возвращается нулевой результат.

Таким образом, вопрос для меня заключается в том, может ли SDK иметь возможность также обрабатывать этот случай, или же ошибка заключается в том, что S / 4HANA API не возвращает результат, соответствующий ODATA.

1 Ответ

0 голосов
/ 05 июля 2019

К сожалению, нет простого способа настроить это поведение при использовании API.Как вы уже заметили, текущая реализация ожидает, что результат JSON будет содержать объект с тем же именем ключа, что и имя import , вызываемое в запросе OData.

Вот временный обходной путьВаша проблема:

  • Переопределите класс реализации FluentHelperFunction следующим методом getFunctionName:

    @Override
    @Nonnull
    protected String getFunctionName()
    {
        final String callingMethod = Thread.currentThread().getStackTrace()[2].getMethodName();
        if( "generatePath".equals(callingMethod) ) {
            return "TheFunctionNameInUrlPath";
        }
        if( "executeSingle".equals(callingMethod) ) {
            return "TheKeyOfODataResponse";
        }
        throw new IllegalStateException("This should not happen.");
    }
    

Покаэто не очень хороший код, мы проверим, как это можно упростить в будущем с помощью SAP Cloud SDK.


Обновление: К сожалению, ответ JSON, который вы получаете,не оборачивает содержимое в другой объект внутри корневого элемента "d".Это значительно усложняет решение проблемы.

Если вы все еще хотите использовать SAP Cloud SDK в этом сценарии, вам потребуется адаптировать некоторый внутренний код.Вместо перечисленного выше getFunctionName попробуйте следующее изменение метода execute (или executeSingle):

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import com.google.common.io.CharStreams;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.json.JsonSanitizer;
import com.sap.cloud.sdk.odatav2.connectivity.ODataExceptionType;
import com.sap.cloud.sdk.odatav2.connectivity.ODataGsonBuilder;
import com.sap.cloud.sdk.result.GsonResultElementFactory;
import com.sap.cloud.sdk.result.ResultElement;
import org.apache.http.HttpEntity;

...

    @Override
    @Nullable
    public T execute( @Nonnull final ErpConfigContext configContext )
        throws ODataException
    {
        final HttpEntity httpEntity = accessibleQuery(configContext);

        final ProposalHeader response;
        try( final InputStream content = httpEntity.getContent() ) {

            final String rawContent = CharStreams.toString(new InputStreamReader(content, StandardCharsets.UTF_8));
            final JsonElement responseJsonElement = new JsonParser().parse(JsonSanitizer.sanitize(rawContent));

            // select JSON root object
            final JsonElement jsonElement = responseJsonElement.getAsJsonObject().getAsJsonObject("d");

            // deserialize contents
            final GsonResultElementFactory elementFactory = new GsonResultElementFactory(ODataGsonBuilder.newGsonBuilder());
            final ResultElement resultElement = elementFactory.create(jsonElement);
            response = resultElement.getAsObject().as(getEntityClass());
        }
        catch( final IOException | IllegalArgumentException | JsonSyntaxException e ) {
            throw new ODataException(ODataExceptionType.ODATA_OPERATION_EXECUTION_FAILED, "Failed to read OData result.", e);
        }
        return response;
    }

    private HttpEntity accessibleQuery( @Nonnull final ErpConfigContext configContext ) {
        try {
            final Method query = FluentHelperFunction.class.getDeclaredMethod("query", ErpConfigContext.class);
            query.setAccessible(true);
            Object httpEntityRaw = query.invoke(this, configContext);
            return HttpEntity.class.cast(httpEntityRaw);
        }
        catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassCastException e) {
            // log error
            // throw exception
            return null;
        }
    }

Вы можете изменить универсальный тип T на ожидаемый класс ответа,Также измените строки // log error и // throw exception, чтобы они соответствовали сценарию использования вашего приложения, чтобы в будущем можно было легко выполнять обработку ошибок.Кроме того, вы должны ввести в этот код некоторые проверки null.

...