3

I’m relatively new to Apama. I’m using v10.3.1. I’m following the following snippet to do a REST request within a monitor:

http://www.apamacommunity.com/documents/10.3.1.1/apama_10.3.1.1_webhelp/apama-webhelp/#page/apama-webhelp%2Fco-ConApaAppToExtCom_httpclient_using_predefined_generic_event_definitions.html%23wwconnect_header

The action to handle the response currently looks as follows:


action handleResponse(Response response){
        if response.isSuccess(){
            print "###The response payload is :" + response.payload.toString();
        }

        else {
            print "###Request failed. Response status is: " + response.statusCode.toString() + " | " + response.statusMessage;
        }
    }

I’m looking for the best way to extract the value of the following property within the JSON response payload:

assetparents.references[0].managedObject.name (here “SOME”).

I tried different ways but always ran into an error.

The print statement provides the following output for the response payload:



###The response payload is :com.apama.util.AnyExtractor(any(string,"

{"owner":"some@one.com","additionParents":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/additionParents","references":[]
},
"childDevices":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/childDevices","references":[]
},

"childAssets":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/childAssets","references":[]
},
"creationTime":"2019-05-09T11:36:10.197Z",
"lastUpdated":"2019-05-10T05:28:07.893Z",
"childAdditions":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/childAdditions",
    "references":[{
        "managedObject":{"name":"Escalate alarmtest",
        "self":"https://somebaseurl/inventory/managedObjects/5706698",
        "id":"5706698"},
        "self":"https://somebaseurl/inventory/managedObjects/5706999/childAdditions/5706698"
    }
]},
"name":"SOME Test Device",
"deviceParents":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/deviceParents",
    "references":[]
},
"assetParents":{
    "self":"https://somebaseurl/inventory/managedObjects/5706999/assetParents",
    "references":[{
        "managedObject":{
            "additionParents":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/additionParents",
                "references":[]
            },
            "childDevices":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/childDevices",
                "references":[]
            },
            "childAssets":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/childAssets",
                "references":[]
            },
            "childAdditions":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/childAdditions",
                "references":[]
            },
            "name":"SOME",
            "deviceParents":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/deviceParents",
                "references":[]
            },
            "assetParents":{
                "self":"https://somebaseurl/inventory/managedObjects/5706682/assetParents",
                "references":[]
            },
            "self":"https://somebaseurl/inventory/managedObjects/5706682",
            "id":"5706682"
        },
        "self":"https://somebaseurl/inventory/managedObjects/5706999/assetParents/5706682"
    }]
},
"self":"https://somebaseurl/inventory/managedObjects/5706999",
"id":"5706999",
"c8y_ActiveAlarmsStatus":{
    "minor":0,
    "critical":1
},
"c8y_IsDevice":{},
"ax_Customer":"SOME CUSTOMER",
"c8y_SupportedMeasurements":["c8y_Temperature"]}"))

Besides parsing the individual property, what is the recommended way to map the entire object to an Apama event?

If you could provide a code snippet it would be greatly appreciated.

Many thanks Mathias

Mathias
  • 85
  • 1
  • 5

2 Answers2

2

To answer your first question.

The output of the print statement is very helpful. It shows that the response payload is raw JSON string instead of parsed JSON object. This means that JOSN codec skipped decoding the JSON srting. This will happen whe using the "JSON with generic request/response event definitions" plug-in, if the contentType header is not exactly application/json. Testing with Cumulocity revealed that the value of the header is not always application/json, instead it could be something like application/vnd.com.nsn.cumulocity.managedobject+json or application/vnd.com.nsn.cumulocity.measurementcollection+json which current JSON codec can't handle.

There are two ways you can deal with it without any changes in the JSON codec.

1) Disable the filtering of messages by JSON codec

The JSON codec used by "JSON with generic request/response event definitions" plug-in is configured to skip decoding messages which does not have contentType header set to application/json. This can be disabled by editing the HTTPClient.yaml file under "Connectivity and Adapters/HTTP Client/HTTPClient" node in the Designer project. Set the filterOnContentType property to false. This will cause all responses to be processed as JSON; if the payload is not JSON, parsing will fail and message will be dropped. So enable this only if you are sure that all responses will be JSON.

2) Parse JSON strings in EPL

Other option could be to parse the string to JSON in the EPL itself and then wrap the JSON object into the AnyExtractor for extracting the required value. You can use something like following to do that.

using com.apama.json.JSONPlugin;
using com.apama.util.AnyExtractor;
...
action handleResponse(Response res) {
    if res.isSuccess() {
        log "Response is: " + res.toString();
        // Check if payload is string. A string payload could suggest raw JSON string
        switch (res.payload.data as payload) {
            case string: {
                // Parse the JSON string manually
                AnyExtractor extractor := AnyExtractor(JSONPlugin.fromJSON(payload));
                string name := extractor.getString("assetparents.references[0].managedObject.name");
            }
            default: { 
                // probably already parsed to JSON - use AnyExtractor to work on it
            }
        }
    } else {
        log "Failed: " + res.statusMessage at ERROR;
    }
}
Gyan
  • 66
  • 2
  • Many thanks for your help and your comprehensive answer, very much appreciated, Gyan. Your suggested solution worked like a charm. Kind regards Mathias – Mathias May 17 '19 at 14:56
2

To address your second question, namely "what is the recommended way to map the entire object to an Apama event?":

I've defined several events which will map to the JSON you provided:

event Reference {
    string self;
    any managedObject;
}

event Object {
    string self;
    sequence<Reference> references;
}

event ActiveAlarmsStatus {
    integer minor;
    integer critical;
}
event IsDevice {}

event ManagedObject {
    string owner;
    string self;
    string id;
    string name;
    string creationTime;
    string lastUpdated;
    Object additionParents;
    Object childDevices;
    Object childAssets;
    Object childAdditions;
    Object deviceParents;
    Object assetParents;
    ActiveAlarmsStatus c8y_ActiveAlarmsStatus;
    IsDevice c8y_IsDevice;
    string ax_Customer;
    sequence<string> c8y_SupportedMeasurements;
}

Because a ManagedObject contains an Object, which contains a Reference which itself contains a ManagedObject, the EPL wouldn't compile due to the recursive types. Therefore, in Reference we use an any type to mask the ManagedObject. this allows the EPL to compile.

However, because an any hides the type, we don't know what type to convert it to, and so instead we have an any containing a dictionary. This is fine though, because we can use some helper functions to extract the information we want:

action GetSequenceReference(sequence<any> s) returns sequence<Reference> {
    sequence<Reference> ret := new sequence<Reference>;
    any r := new any;
    for r in s {
        ret.append(<Reference>r);
    }
    return ret;
}
action GetSequenceString(sequence<any> s) returns sequence<string> {
    sequence<string> ret := new sequence<string>;
    any r := new any;
    for r in s {
        ret.append(<string>r);
    }
    return ret;
}

action GetObject(any a) returns Object {
    log "Getting object from: " + a.toString() at INFO;
    dictionary<any,any> dict := <dictionary<any,any> >a;

    Object ret := new Object;

    ret.self := <string>dict.getDefault( "self", "" );
    ret.references := GetSequenceReference( <sequence<any> >dict.getDefault( "references", new sequence<any> ) );

    return ret;
}

action GetActiveAlarmsStatus(any a) returns ActiveAlarmsStatus {
    log "Getting active alarms status from: " + a.toString() at INFO;
    dictionary<any,any> dict := <dictionary<any,any> >a;

    ActiveAlarmsStatus ret := new ActiveAlarmsStatus;

    ret.minor := <integer>dict.getDefault( "minor", 0 );
    ret.critical := <integer>dict.getDefault( "critical", 0 );

    return ret;
}
action GetIsDevice(any a) returns IsDevice {
    log "Getting is device from: " + a.toString() at INFO;
    dictionary<any,any> dict := <dictionary<any,any> >a;

    IsDevice ret := new IsDevice;

    return ret;
}
action GetManagedObject(any a) returns ManagedObject {

    log "Getting managed object from: " + a.toString() at INFO;
    dictionary<any,any> dict := <dictionary<any,any> >a;

    ManagedObject ret := new ManagedObject;
    ret.owner := <string>dict.getDefault( "owner", "" );
    ret.self := <string>dict.getDefault( "self", "" );
    ret.id := <string>dict.getDefault( "id", "" );
    ret.name := <string>dict.getDefault( "name", "" );
    ret.creationTime := <string>dict.getDefault( "creationTime", "" );
    ret.lastUpdated := <string>dict.getDefault( "lastUpdated", "" );
    ret.additionParents := GetObject( dict.getDefault( "additionParents", new dictionary<any,any> ) );
    ret.childDevices := GetObject( dict.getDefault( "childDevices", new dictionary<any,any> ) );
    ret.childAssets := GetObject( dict.getDefault( "childAssets", new dictionary<any,any> ) );
    ret.childAdditions := GetObject( dict.getDefault( "childAdditions", new dictionary<any,any> ) );
    ret.deviceParents := GetObject( dict.getDefault( "deviceParents", new dictionary<any,any> ) );
    ret.assetParents := GetObject( dict.getDefault( "assetParents", new dictionary<any,any> ) );
    ret.c8y_ActiveAlarmsStatus := GetActiveAlarmsStatus( dict.getDefault( "c8y_ActiveAlarmsStatus", new dictionary<any,any> ) );
    ret.c8y_IsDevice := GetIsDevice( dict.getDefault( "c8y_IsDevice", new dictionary<any,any> ) );
    ret.ax_Customer := <string>dict.getDefault( "ax_Customer", "" );
    ret.c8y_SupportedMeasurements := GetSequenceString( <sequence<any> >dict.getDefault( "c8y_SupportedMeasurements", new sequence<any> ) );
    return ret;
}

Using these helper functions, we can extract the information from our original event:

action handleResponse(Response response){
    if response.isSuccess(){
        ManagedObject mo := GetManagedObject( response.payload.toDictionary() );
        log "###The response payload is :" + mo.toString() at INFO;
    }

    else {
        log "###Request failed. Response status is: " + response.statusCode.toString() + " | " + response.statusMessage at INFO;
    }
}

And there you have it! A fully mapped event.