Accessing Data
Accessing data of your module and other modules can be done using the AOM object of the main class of your module. Alternatively, the "model" object in each hook class and every object of the type "AbstractClientDataModel" offer the same methods.
For the majority of method calls, a Request object is required as a parameter which contains data about the current user request. It is be provided in the methods of Hook classes by ApiOmat as an argument and is usually called "r". Also, there are many methods that need to know the name of the application that possesses the data to be accessed. The application name can be retrieved from a Request object and is given in many methods of the main class of the module. It will be assumed that the String variable "appName" contains the application name.
Contents
Authentication
By default, all calls inside native module code are authenticated using the account which owns the app.
If you want to use the credentials in the request for authentication, just switch the authentication mechanism in the ApiomatRequest object r:
...
r.authenticateUser =
true
;
...
MyModule.AOM.findById ( appName,
"12345678"
,
"Basic"
,
"User"
, r);
All further calls in this thread will be authenticated with the user in the request.
Log messages
MyModule.AOM.log( String appName, String message );
// or using an object directly
obj.log( String message );
will log the given message with a timestamp to the logging console. Example:
MyModule.AOM.log( appName,
"My value is: "
+ i );
// or using an object directly
ob.log(
"My value is: "
+ i );
Log models and their methods
MyModule.AOM.logModel( String appName, String message, Object model )
// or using an object directly
ob.logModel( String message, Object model );
will print the name of the model and its public methods on the logging console. You can also provide an array of objects. Example:
MyModule.AOM.logModel( appName,
"My object is: "
, myObj);
// or using an object directly
ob.logModel( String message, Object model );
Get an object with id and classname
MyModule.AOM.findById( String appName, String id, String moduleName, String className, Request r );
// or using an object directly
ob.findById( String id, String moduleName, String className, Request r );
Example:
User user= MyModule.AOM.findById ( appName,
"12345678"
,
"Basic"
,
"User"
, r);
// or using an object directly
ob.findById(
"12345678"
,
"Basic"
,
"User"
, r );
Or with a foreignID:
MyModule.AOM.findByForeignId( String appName, String foreignId, String moduleName, String className, Request r )
// or using an object directly
ob.findById( String foreignId, String moduleName, String className, Request r );
Add/Get/Remove referenced objects
Let's say you have a class “Car” with some references to class “Tire”, then you can add a tire to a car using the method:
myCar.postTire(myTire);
The same works for remove and get:
myCar.removeTire(myTire);
myTire = myCar.getTire();
Add/Get/Remove referenced static data
Let's say you have a class “Message” with an attribute of type "File" called "attachment”, then you can add an attachment to a message using the method:
message.addReferencedData(
"attachment"
, inputStream,
"attachment_2017-10-18"
,
"zip"
);
The same works for get and delete:
// Load data
DataWrapper dataWrapper = message.loadReferencedData(
"attachment"
, dataId );
InputStream data = dataWrapper.getData();
// Load image data with transcodingConfig (for changing size, background color, image format, ...)
dataWrapper = message.loadReferencedData(
"attachment"
, dataId, transcodingConfig );
data = dataWrapper.getData();
// Delete data
message.deleteReferencedData(
"attachment"
, dataId );
Create an object
When using Native Modules, you can create objects of arbitrary types only with the createObject method:
MyModule.AOM.createObject( String appName, String moduleName, String className, Request r );
The return value is of type IModel<?> and needs to be cast to the type you're creating.
Example:
User user = (User) MyModule.AOM.createObject(appName,
"Basics"
,
"User"
, r);
user.setUserName(
"joe"
);
user.setPassword(
"123456"
);
user.save();
Get all objects of class, filtered by a query
Querys are explained in our apidocs:
To get objects of your any class use
MyModule.AOM.findByNames( String appName, String moduleName, String className, String query, Request r )
Please pay attention that an ARRAY is returned.
Example:
resultArray = MyModule.AOM.findByNames( appName,
"Basics"
,
"User"
,
"age>20"
, r )
DeltaSync
When using findByNames in Native Module Code, you can utilize DeltaSync to minimize the amount of data being sent from the server to client applications.
This segment only explains how to use DeltaSync in Native Module Code. For general and client-side info see DeltaSync.
DeltaSync only makes sense when querying collections, so as an example, we'll use the doGetAll() hook method for transient classes:
public
List<SomeTransientClass> doGetAll( String query, Request r )
When the client application uses one of our SDKs that supports DeltaSync and DeltaSync is activated in the client Datastore, then the request to the getAll endpoint contains a DeltaSync header. This header contains a JSON object which maps foreignIDs to lastModified timestamps. Depending on how you modeled your data, you might want to map your transient objects foreignIDs to other non-transient object IDs, so you need to change the DeltaSync header. The same applies to the IDs contained in the header for deleted IDs. So for working with all these, the Request object offers the following methods:
public
String getDeltaSyncHeaderValue( )
// Returns the JSON object as String
public
void
setDeltaSyncHeaderValue( String deltaSyncHeaderValue )
public
Map<String, Long> getDeltaSyncMap( )
// Returns the JSON object as map, which makes it easier for you to work with it
public
void
setDeltaSyncMap( Map<String, Long> deltaSyncMap )
public
String getDeltaSyncDeletedHeaderValue( )
// Returns the JSON array as String
public
void
setDeltaSyncDeletedHeaderValue( String deltaSyncDeletedHeaderValue )
public
List<String> getDeltaSyncDeletedList( )
// Returns the JSON array as list, which makes it easier for you to work with it
public
void
setDeltaSyncDeletedList( List<String> deltaSyncDeletedList )
public
boolean
isUseDeltaSyncInFindByNames()
public
void
setUseDeltaSyncInFindByNames(
boolean
useDeltaSyncInFindByNames )
// Needs to be set to true if you want the DeltaSync info in the Request
// object to be used when calling findByNames
public
boolean
isUseDeltaSyncDeletedInResponse( )
public
void
setUseDeltaSyncDeletedInResponse(
boolean
useDeltaSyncDeletedInResponse )
// Needs to be set to true if you want the DeltaSync-deleted info
// in the Request object to be used in the response to the client
Also read the Javadoc of the shown methods in the Request class.
A typical use-case is that you have a transient class that accesses a legacy backend system, for example SAP, and you have a non-transient class for caching (remember, objects of non-transient classes are stored in ApiOmat directly, so this is much faster). When a client request arrives, you deliver the cached data, and only if some condition is met (e.g. 15 minutes are passed since fetching from the legacy backend system), you directly fetch from the legacy backend system.
Regarding the above mentioned use-case a simple example, where the foreignIds of the objects you deliver to the client in the response are the same as the cached object's IDs, would look like this:
@Override
public
List<SomeTransientClass> doGetAll( String query, Request r )
{
// Some logic that decides whether to use cached data or fetch data from the legacy backend system
// ...
// Use cached data here:
r.setUseDeltaSyncInFindByNames(
true
);
IModel<?>[] foundModels = YourModule.AOM.findByNames( r.getApplicationName(), YourCachingClass.MODULE_NAME, YourCachingClass.MODEL_NAME, query, r );
r.setUseDeltaSyncDeletedInResponse(
true
);
return
Stream.of(foundModels)
.map( foundModel -> {
YourCachingClass actualModel = ( YourCachingClass ) foundModel;
SomeTransientClass returnModel = ( SomeTransientClass ) YourModule.AOM.createObject(
r.getApplicationName( ), SomeTransientClass.MODULE_NAME, SomeTransientClass.MODEL_NAME );
returnModel.setForeignId( actualModel.getId( ) );
returnModel.setLastModifiedAt( actualModel.getLastModifiedAt( ) );
// Copy all attributes of the cached object to the transient object
returnModel.setSomeAttribute( actualModel.getSomeAttributes( ) );
return
returnModel;
} )
.collect( Collectors.toList( ) );
}
It's a bit more difficult when you want to use different foreignIds than the cached object's IDs:
-
You'd have to have some kind of mapping logic that maps foreignIds to cached object IDs and vice versa.
-
You need to apply the mapping from foreignID to cached object ID to the DeltaSync map and set it before calling findByNames()
-
You need to apply the reverse mapping from cached object ID to foreignID to the DeltaSync-deleted list before returning the result
Another note regarding setUseDeltaSyncInFindByNames and setUseDeltaSyncDeletedInResponse: They are turned off by default, because if the client makes a request to your class X, but in your doGetAll implementation you make a call to a class Y, that's not a caching class but something totally different, than the DeltaSync map and DeltaSync-deleted list make no sense here and instead might mess up the DeltaSync-deleted header in the response to the client. Also, when making multiple calls to findByNames() with different target classes, and you use the DeltaSync map in the first call, you might want to set setUseDeltaSyncInFindByNames to false before the next call to another class, for the same reason.
Save and delete objects
This method persists an object:
obj.save()
This one deletes the current object:
obj.delete()
Delete objects by a query
Querys are explained in detail in the following documentation page: Query.
To delete a number of objects filtered by a query, use:
MyModule.AOM.deleteByNames( String appName, String moduleName, String className, String query, Request r )
Exceptions
Maybe you want to check things before creation or update of your models take place. To abort the usual program flow you can throw exceptions in your Native Module Code:
MyModule.AOM.throwException ( String appName, String message );
The Exception message will be displayed in the logs. This request will also throw an exception with the status SCRIPT_EXCEPTION and your provided message.
Obtaining a user and verifying a request
In every hook-class-method you may access the requesting user´s email via the request:
final
String userNameOrEmail = r.getUserEmail( );
As described in the above section, you may get the User object for the respective requester. You may also perform the verifiyHttpRequest method on any object to check if the user is allowed to perform this request.
ApiOmat will call an internal method verifiyHttpRequest for every request to check if the user is allowed to perform this action. The method will check for the model in question for different aspects:
-
First, it will check if the model is visible to the user. If so, the method will retrieve all Authentication classes for this app.
-
Depending on whether any auth classes are set for the app:
-
No auth classes found: Dynamic Roles role classes for the MetaModel of the object are retrieved. Depending on whehter any are found:
-
No role classes set: Default auth is executed, verifying the user with his credentials and checking either simple roles like "User" or "Owner" or otherwise checking ACLs (role objects) if the current requesting user is a member of them
-
Role class set: For custom role classes their isUserInRoles() method gets called, going through all classes until one returns true, or if all return false an authorization error response is sent. For the "Basics$User" the default authorization is executed, checking either simple roles like "User" or "Owner" or otherwise checking ACLs (role objects) if the current requesting user is a member of them
-
-
Auth class found: In order of those classes it will try to verify the user against every single one of them until one returns true implying that the authentication was successful. In the process the method determines whether the authentication-class uses Basics-authentication, simply checking the ApiOmat username/password-combination, or a custom authentication-method. Consequently, it will then apply the respective method.
-
Only if any auth class returns true, it gets checked if a role class is set for the MetaModel of the object and if yes, the above mentioned logic is executed (see "Role class set").
-
-
If the request should use OAuth2-authentication, the token will already be validated in this phase and requests with invalid tokens will already be rejected. But the developer of the authentication-class may still want his auth-method to be called for valid tokens by setting callAuthWithValidToken = true.
Accessing the App-configuration
You may retrieve information on the app´s structure inside your native modules by using the following method from the AOM-interface:
final
String appConfig = MyModule.AOM.readAppConfig( appName );
The configuration is returned as a JSON-confirm String which holds all the app´s modules according to the system (LIVE, STAGING, TEST) together with their type, the module´s classes and consequently the classes´ attributes together with their type.
The access to this data is read-only.
Accessing data of a foreign module
Usually, when you want to access objects of a class that's in module A from module B, you would add module A to the usedModules of module B, allowing you to work with A's classes in the same way as you work with B's classes. But in some cases that might not be possible, for example when A gets created during the runtime of module B.
You can still use methods like findBy...(), with module and model names of foreign modules (not in usedModules list), but you won't be able to cast the returned objects of type Object to the actual classes. Without the correct classes, you would usually have to use reflection to access the object's attributes. But to make it easier for you, you can use the following methods on any instance of IModel instead:
// return all values of this model as a map; the key is the field name
Map<String, Object> getValueMap( )
//return all types of this model as a map; the key is the field name
Map<String, String> getTypeMap( )
Session Data
The session map (Map<String, Object>) allows to store data during the lifetime of a request made against your module.
For example if you call another module from your module code and want to pass additional data you can put it into the session map and retrieve it afterwards.
You can edit the data in the session map as follows:
// override the session map with the given map
MyModule.AOM.setSession( sessionMap );
// append a specific value to the session map
MyModule.AOM.putSessionObject(
"key"
, valueObject );
// delete a specific value from the session map
MyModule.AOM.removeSessionObject(
"key"
);
Retrieving data from the session map is done like this:
// load the complete session map
Map<String, Object> sessionMap = OtherModule.AOM.getSession( );
// load a specific value from the session map
Object valueObject = OtherModule.AOM.getSessionObject( key );