Logging Interface Access

I hope this message finds you well.

I would like to log the event of an interface being opened.
However, my understanding is that on the interface itself, unless an action such as clicking a button occurs,

it is not possible to write a record or start a process model.

When consulting ChatGPT, I received the following idea:

  1. Create a Web API on Appian that returns an image as the response and simultaneously saves a log.
  2. Place an image on the target interface and set the endpoint of the Web API created in (1) as the image source.
  3. As a result, when the interface is opened, the Web API in (1) is called, and the log is recorded.

However, it seems that the function a!writeToDataStoreEntity does not get triggered inside the Web API.

I have two questions and would appreciate your advice:

  1. What should I review to implement the above ChatGPT idea successfully?
  2. Are there alternative methods to log when an interface is opened?

Thank you in advance for your kind advice.

  Discussion posts and replies are publicly visible

Parents
  • Thank you all for your advice.

    Although I initially lacked some information, ultimately, I would like to calculate usage rates for each application from the log data. Therefore, I prefer to save the logs as records rather than text files. Additionally, since I need to calculate usage rates by organization, user information is also necessary.

    Hence, I believe the approach Mike has already implemented is the closest to what I need.

    I implemented the Web API as shown below. While it returns an image file as the response, it seems that neither a!startProcess, a!writeToDataStoreEntity, nor a!writeRecords functions are executing.

    I implemented it using the GET method to return the image file. Is there anything else I should do?


    a!localVariables(

      local!codea: 200,
      local!codeb: 200,
      local!codec: 200,

      local!a: a!writeToDataStoreEntity(
        dataStoreEntity: cons!XLOG_EVENT,
        valueToStore: 'type!{urn:com:appian:types:XLOG}XLOG_RT_EVENT'(
          timestamp: now(),
          appName: "Application Name",
          functionName: "Function Name",
          user: tostring(loggedInUser())
        ),
        onSuccess: a!save(local!codea, 201),
        onError: a!save(local!codea, 202)
      ),

      local!b: a!writeRecords(
        records: 'recordType!{}XLOG rtEvent'(
          'recordType!{}XLOG rtEvent.fields.{}timestamp': now(),
          'recordType!{}XLOG rtEvent.fields.{}appName': "Application Name RT",
          'recordType!{}XLOG rtEvent.fields.{}functionName': "Function Name RT",
          'recordType!{}XLOG rtEvent.fields.{}user': tostring(loggedInUser())
        ),
        onSuccess: a!save(local!codeb, 201),
        onError: a!save(local!codeb, 202)
      ),

      local!c: a!startProcess(
        processModel: cons!XLOG_pmTest,
        onSuccess: a!save(local!codec, 201),
        onError: a!save(local!codec, 202)
      ),

      local!d: a!richTextDisplayField(
        value: "code: " & local!codea & " " & local!codeb& " " & local!codec
      ),

      a!httpResponse(
        statusCode: local!codec,
        headers: {
          a!httpHeader(name: "Content-Type", value: "image/png"),
          a!httpHeader(name: "Cache-Control", value: "no-store, no-cache, must-revalidate, max-age=0"),
          a!httpHeader(name: "Pragma", value: "no-cache")
        },
        body: cons!XLOG_PNG
      )
    )

    NOTE:In this sample code, these local variables are only for debbuging.



  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    You web API structure as per this sample code doesnt seem right! the smart servcies should not be related to a local variable. 

    Please check the documentation for steps/considerations. In you use case you can create a web API that has a!startProcess() in it. Whatever you are trying to accomplish in a!writeRecord() and a!writeToDataStoreEntity() should be moved inside the process. 

    The https Response configuration would also come within the onSuccess or onError of a!startProcess() configuration.

  • Dear Harsha,

    Thank you for your advice.

    I reviewed the documentation again, modified my code and confirmed that a!startProcess() and a!writeRecord can be executed.

    However, as mentioned in my initial post,
    (1) Create a Web API on Appian that returns an image as the response and simultaneously saves a log.
    (2) Place an image on the target interface and set the endpoint of the Web API created in (1) as the image source.
    (3) As a result, when the interface is opened, the Web API in (1) is called, and the log is recorded.

    Since we intend to implement this behavior, the Web API must be implemented with the GET method.

    As a result, we encountered the error:
    "Smart Services cannot be executed in Web APIs that have a method of 'GET'."

    and cannot proceed further.

    Is there any way to work around this?

  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    Hi Keizo,

    It is not a hard requirement to write records from a Process Model. For APIs that are intended to be called frequently, it is better in practice to call a!writeRecords() from the API object itself to remove the overhead and latency of starting a process.

    Keep in mind that, if enabled in the record types, you can write multiple related records at once if they are sync'ed records and have a relationship defined. For me, this has worked for 99% of my use cases. For the other 1%, I did something like the following: 

     

    a!writeRecords(
        records: {Record A and its embedded related records}
        onSuccess: a!writeRecords(
            records: {Record not related to record A}
            onSuccess: a!httpResponse()
            onError: a!httpResponse()
        ),
        onError: a!httpResponse()
    )

  • Thanks Jesse.

    I understood that. But the error "Smart Services cannot be executed in Web APIs that have a method of 'GET'." is still remaining...

  • 0
    Certified Lead Developer
    in reply to Keizo Watsuji

    Web APIs using 'GET' method cannot call any smart services as per Appian Design. So for intended functionality use the 'POST' method in web API and setup an Integration that calls your web API from target interface. As soon as interface loads, the Integration (executed on load against a local variable) will call the API. API will execute the smart service and record log with a success response for integration. When setting up a!httpResponse for web API in response body you can send back the image as well.

    a!httpResponse(
      statusCode: 200,
      headers: {
        a!httpHeader(name: "content-type", value: "application/json")
      },
      body: a!iconIndicator("STATUS_WARN")
    )

    However as earlier discussed in this thread that to log the interface load activity you don't necessarily need to follow what ChatGPT suggested. 

    The web API can write to db and respond back with a successful message. That web API can be called using an integration in the interface. So on load the integration->web API->write records will execute. On Error of API execution, response can be a user friendly message asking them to re-load the interface.

    Is there any reason for image to be involved in the response and showing it on interface if you just want to log the interface load activity? 

  • 0
    Certified Lead Developer
    in reply to Harsha Sharma

    With the POST, be sure to select 'queries data', and this should work ok. If you select 'modifies data', you get an error related to the fact that the integration call is not in a saveInto.

    A possible quirk about retrieving images via API: We don't know if there are performance issues at scale when retrieving images directly via API versus using a!documentImage().So to avoid any potential issues when using an image in this way, it may be best for the API to return a document id for use with a!documentImage(). That keeps the API layer small / concise, and keeps the performance of the UI expression in line with what's generally expected when many users load the image.

       One possible reason to make this an image retrieval is so that there's a reusable UI component that's placeable in any UI, including collapsible sections / hidden parts of the UI that are conditionally shown. The reusable component would receive any metadata about the section context, automatically call the API on demand, and load a 1x1 pixel image with no padding / borders onto the UI. Without the UI component aspect, the logging isn't very reusable as an encapsulated rule.

Reply
  • 0
    Certified Lead Developer
    in reply to Harsha Sharma

    With the POST, be sure to select 'queries data', and this should work ok. If you select 'modifies data', you get an error related to the fact that the integration call is not in a saveInto.

    A possible quirk about retrieving images via API: We don't know if there are performance issues at scale when retrieving images directly via API versus using a!documentImage().So to avoid any potential issues when using an image in this way, it may be best for the API to return a document id for use with a!documentImage(). That keeps the API layer small / concise, and keeps the performance of the UI expression in line with what's generally expected when many users load the image.

       One possible reason to make this an image retrieval is so that there's a reusable UI component that's placeable in any UI, including collapsible sections / hidden parts of the UI that are conditionally shown. The reusable component would receive any metadata about the section context, automatically call the API on demand, and load a 1x1 pixel image with no padding / borders onto the UI. Without the UI component aspect, the logging isn't very reusable as an encapsulated rule.

Children
No Data