
028: Recycling-Heroes - Configuration API (Settings)
To easily access the business configuration data, we define an API that we can then reuse within the system. The various ABAP artifacts are also explained in more detail here.
Table of contents
Introduction
After creating the business configuration, we now need an API in the system to access the content. For our later tests, some values have already been entered, which we can then use for access. For fields with an X, we want to check if the feature is active, and for the other values, we want the actual values.
Creation
We create the new API in the SHARE package, as we want to make it available to other developments later. We use the IDE action to create the new classes, as the action already automates some of the generation process. We specify the object name, description, and instance name. The object names are then automatically suggested, and we accept these. Once generation is complete, we get an overview of all objects and can navigate to them.
To do this, we first call the factory. This will later create the instances for us to access the current configuration. Additionally, the factory has the injector as a global friend. This relationship is needed so that the injector can override the `Double` attribute in the class. The actual `Create` method returns a new instance of the configuration or the `Double` if it is set.
Now let's look at the injector. Since it is marked as `FOR TESTING`, it can only be used in the case of unit tests. The injector ensures that we get test doubles into the process when we want to run unit tests. Outside of unit tests, the class cannot be used, which is a safeguard for us to prevent anyone from injecting incorrect objects into the production environment.
Interface
Let's now take care of implementing the interface. We need various types, constants, and methods for later implementation. Therefore, let's start with the constant structure for the different settings that we have previously stored as fixed values in the domain. We can give the different fields descriptive names, as we are not limited to 10 characters here. As a second constant structure, we define the various processes available to us in Customizing. We perform this step primarily to ensure access to all constant values and to provide the user with a clean interface. This delimitation can be used later for authorizations, but is not needed in our case for now.
Later, the GET_VALUE method will return a single value from Customizing, in this case, the value in the Value field. For subsequent read accesses, we need a corresponding type that reflects our Core Data Service. The second method, GET_VALUES, will generate a corresponding range from the values if we have more than one row and a range of values. As a third method, we define IS_ACTIVE, which should simply return a Boolean value to identify the function and whether it is active in the system.
Now let's fix the data type for our two VALUE Methods, because we want to return the Value and a Range of Values. After this, we can format and save the Interface.
Implementation
In the class, we now implement the various methods via the interface. To retrieve the values, we implement a private method to read the Core Data Service and return the found values. In the SELECT statement, we delimit the different configurations and use PRIVILEGED ACCESS to bypass the access control. This ensures that the values can always be read during process execution, even if the user doesn't have the necessary permissions to view or maintain them at that moment.
Let's now implement the GET_VALUES method and call the new method to read all the data. In the next step, we want to populate the range and must react accordingly to the maintained values. We only want to process rows where one of the two value fields is populated. Then we insert an initial row into the range and set a reference to populate the other fields subsequently. We then compare the fields; if both fields are populated, we accept them and add a BETWEEN clause to the option. We pass the other fields to LOW and populate the option with EQUAL. We could resolve the warning with an initial value in the option, but for now, we'll set it later.
Now let's implement the GET_VALUE method. Here, we only want to read one value and return it to the caller. To do this, we again read the corresponding configuration and then have the first line returned. For this case, we use a VALUE statement and set the value to OPTIONAL. If, for example, no entry is found because the configuration is unavailable, then an empty structure is returned. Finally, we return the value of the structure.
As a last step, we implement the IS_ACTIVE method to check whether the indicator is set to ABAP_TRUE and thus the function is active. For this, we use the GET_VALUE method to retrieve the configuration value and then compare it against ABAP_TRUE. We then return the result to the caller.
Test
To easily test the implementation, we create a local unit test in the class. This allows us to easily and thoroughly test our implementation. To do this, we switch to the "Test Classes" tab in the lower part of the class. Using the TESTCLASS template, we create an empty test class. We can navigate through the individual components using the tab key and rename the method. Then we create an instance of our configuration via the factory. We retrieve a value from the object by accessing the class constants through the instance. This allows us to retrieve the DEFAULT_CAPACITY value. As a simple validation, we check if the variable is no longer initial.
We now set a breakpoint in the code and start the unit test using CTRL + SHIFT + F10. In the debugger, we can look at the content of the variable RESULT and find our customization of 120 there. This completes the simple test, and the logic works so far.
Release
In the last step, we want to create a C1 contract on the objects so that we can later use the API in the other software components as well. For this, we need to release all objects except the actual class with the implementation. We need the interface for calling and defining variables, the factory for instantiation, and the injector for decoupling in other tests.
So, let's start with releasing the interface. We can do this directly via the object's context. The release for ABAP Cloud is sufficient here, as we will not be using the class in Key User Extensibility. During the check, we receive numerous warnings because certain return types of the interface are not released. Therefore, we must first modify some types in the interface. Let's create a corresponding local type for the Config ID and the process type and replace the existing types and constants with the new type. This releases the new type externally via the interface, and the constants can now be used with this type. Finally, we also need to create a type for VALUE and replace it in the GET_VALUE method.
If we now release the interface again, all warnings will disappear, and all types that we expose will also be available and released as types in the interface. With this, we can now also release the factory and the injector, and we are finished with the API.
Summary
In today's episode, we created an API for configuration, building it according to all criteria of testability and extensibility. Finally, we have also made the API available to other software components in the system, and they can use it later.
That concludes this. Thank you for your attention, and see you next time.
YouTube
Video