Using Audit Events in Launch

So this post is technical, but it answers a common question or demand: how can I be notified when someone makes or publishes changes in Launch? DTM used to have emails…

The answer is, that as of March 25, email notifications for publish are on the road map for Launch, so there’s nothing built-in right now, but Audit Events can help you build that yourself.

If you read this because you want email notifications: at some point, tptb will make it happen. Until then, grab a techie, ask them to read this article, then talk about what they can do for you.

(You, dear reader, are, of course and by my very own definition, that techie, so please read on.)

Launch Workflow

Launch is the tag management part of the overall Adobe Experience Platform. It’s the way that you inject Javascript into your site. Part of the way you do that is a workflow.

  1. Developers make changes and add them to “Libraries”
  2. Libraries can be submitted for review
  3. Reviewers can approve or reject Libraries
  4. Approved Libraries can be released to production

Each of these steps is, in the context of a bigger organisation, worth monitoring.

DTM, the predecessor to Launch, had a switch in the backend. If enabled, the system would send an email to every administrator every time someone published a Library.

DTM was simpler than Launch is, and so the same mechanism doesn’t quite match, and Launch doesn’t currently have anything like it.

Audit Events

What Launch has, though, are Audit Events.

Everything in and aorund Launch is, deliberately, built with an “API first” approach, meaning the UI usually comes after the core functionality.

The same applies here: the system is able to signal every one of the steps above, but it does so via API, currently, and there is no UI, no front-end, to make it simple to use.

So, if you want notifications whenever a Library is published, you’ll have to write code (Yay!)

The documentation for Audit Events lists 9 resource types and 3 events that you can listen to:

  • property
  • extension
  • data_element
  • rule
  • rule_component
  • library
  • build
  • environment
  • host
  • created
  • updated
  • deleted

The screenshot in the documentation shows an Audit Event of my colleague Ben Mills deleting a property:

[screenshot]
Sample call from the doc
The documentation is also pretty easy to understand when it comes to listing all Audit Events, or fetching a specific one.

What I am missing, now, is how do I subscribe to specific events on specific resource types, such as library.created, or build.created?

And which one do I use when I want to know about Libraries being published on the production Environment?

Well, I guess I must try.

Callbacks

You all know that Postman and I will not be friends, and so I will endeavour to register my callbacks using curl, only.

I start with a look at the documentation, more specifically the part about Creating a Callback.

This will be a POST request to an endpoint at /properties/:property_id/callbacks with two mandatory fields as payload: subscriptions and url.

While the latter is the URL that Launch will call when the Audit Event happens, the former is a list of which resource type-event-combinations this callback should apply to.

The example in the documentation has one subscription: rule.created, i.e. that callback will hit the URL when someone creates a Rule in the Property specified.

[screenshot]
Example from the doc
For my use case, I think I’ll try build.created, build.updated, and build.deleted, and I’ll hit a URL on my server which does nothing but writing the request payload into a file.

So this is my curl command:

curl https://reactor.adobe.io/properties/PR331f44fae6bf4d23a35656120fc9fdd0/callbacks \
  -H "Accept: application/vnd.api+json;revision=1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer [TOKEN]" \
  -H "X-Api-Key: 8d947eb623b44039866685e38e470195" \
  -H "X-Gw-Ims-Org-Id: BE5121AA565821017F000101@AdobeOrg" \
  -X POST \
  -d \
'
{
  "data": {
    "attributes": {
      "url": "https://www.jan-exner.de/orm/hook.php",
      "subscriptions": [
        "build.created",
        "build.updated",
        "build.deleted"
      ]
    }
  }
}'

When I run that, I get a nice reply from Launch, essentially telling me “ok!”

So far, so good.

And then?

The moment I start a build, my hook immediately receives a pretty big package of data:

{
  "data": {
    "type": "audit_events",
    "relationships": {
      "property": {
        "links": {
          "related": "https://reactor.adobe.io/audit_events/AEc1d1e4ec2b4d4c799c7dbe85a7b28f2e/property"
        },
        "data": {
          "type": "properties",
          "id": "PR331f44fae6bf4d23a35656120fc9fdd0"
        }
      },
      "entity": {
        "links": {
          "related": "https://reactor.adobe.io/audit_events/AEc1d1e4ec2b4d4c799c7dbe85a7b28f2e/build"
        },
        "data": {
          "type": "builds",
          "id": "BL995725ac49104fd58eb1ea97416a7a47"
        }
      }
    },
    "meta": {
      "property_name": "jan-exner.de"
    },
    "links": {
      "self": "https://reactor.adobe.io/audit_events/AEc1d1e4ec2b4d4c799c7dbe85a7b28f2e",
      "property": "https://reactor.adobe.io/properties/PR331f44fae6bf4d23a35656120fc9fdd0",
      "entity": "https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47"
    },
    "id": "AEc1d1e4ec2b4d4c799c7dbe85a7b28f2e",
    "attributes": {
      "updated_at": "2020-04-05T20:20:20.004Z",
      "type_of": "build.created",
      "entity": "{\"data\":{\"id\":\"BL995725ac49104fd58eb1ea97416a7a47\",\"type\":\"builds\",\"attributes\":{\"created_at\":\"2020-04-05T20:20:19.938Z\",\"status\":\"pending\",\"updated_at\":\"2020-04-05T20:20:19.938Z\",\"token\":\"1cb048d3b25d\"},\"relationships\":{\"data_elements\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/data_elements\"}},\"extensions\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/extensions\"}},\"rules\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/rules\"}},\"environment\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/environment\"},\"data\":{\"id\":\"ENd2b20a380f784ce89e79a7bb5d77761d\",\"type\":\"environments\"}},\"library\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/library\"},\"data\":{\"id\":\"LB3b9a06fb85dd4aa68b37980624a8334a\",\"type\":\"libraries\"}},\"property\":{\"links\":{\"related\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/property\"},\"data\":{\"id\":\"PR331f44fae6bf4d23a35656120fc9fdd0\",\"type\":\"properties\"}}},\"links\":{\"environment\":\"https://reactor.adobe.io/environments/ENd2b20a380f784ce89e79a7bb5d77761d\",\"library\":\"https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a\",\"self\":\"https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47\"},\"meta\":{\"artifact_url\":\"https://assets.adobedtm.com/launch-ENd2b20a380f784ce89e79a7bb5d77761d-development.zip\",\"direct_artifact_url\":\"https://assets.adobedtm.com/3028746f70eb/45aeacd29ba9/1cb048d3b25d.zip\",\"archive\":true,\"host_type_of\":\"akamai\"}}}",
      "display_name": "BL995725ac49104fd58eb1ea97416a7a47",
      "created_at": "2020-04-05T20:20:20.004Z",
      "attributed_to_email": "jexner@adobe.com",
      "attributed_to_display_name": "Jan Exner"
    }
  }
}

That is really quite a lot of IDs, in there, but if you build some logic to receive these events, why not just add some extra calls that look up those IDs and retrieve some human-readable labels and names?

In my example above, I’m only really interested in the whole “entity” block or the “display_name” in line 37, plus line 40 tells me who triggered the build.

Now, about that “display_name”… can I find out more about that?

Let’s try pulling that build out of Launch, or rather, the Library behind that build. The call for that is pretty simple:

curl https://reactor.adobe.io/builds/BL995725ac49104fd58eb1ea97416a7a47/library \
  -H "Accept: application/vnd.api+json;revision=1" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Authorization: Bearer [TOKEN]" \
  -H "X-Api-Key: 8d947eb623b44039866685e38e470195" \
  -H "X-Gw-Ims-Org-Id: BE5121AA565821017F000101@AdobeOrg" \
  -X GET

The result:

{
  "data": {
    "id": "LB3b9a06fb85dd4aa68b37980624a8334a",
    "type": "libraries",
    "attributes": {
      "created_at": "2020-03-23T15:50:44.475Z",
      "name": "Test",
      "published_at": null,
      "state": "development",
      "updated_at": "2020-03-23T15:50:44.475Z",
      "build_required": false
    },
    "relationships": {
      "builds": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/builds"
        }
      },
      "environment": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/environment",
          "self": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/relationships/environment"
        },
        "data": {
          "id": "ENd2b20a380f784ce89e79a7bb5d77761d",
          "type": "environments"
        }
      },
      "data_elements": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/data_elements",
          "self": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/relationships/data_elements"
        }
      },
      "extensions": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/extensions",
          "self": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/relationships/extensions"
        }
      },
      "notes": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/notes"
        }
      },
      "rules": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/rules",
          "self": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/relationships/rules"
        }
      },
      "upstream_library": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/upstream_library"
        },
        "data": {
          "id": "LBa782f0313b3f429889a0bc47d724c7a6",
          "type": "upstream_libraries"
        }
      },
      "property": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/property"
        },
        "data": {
          "id": "PR331f44fae6bf4d23a35656120fc9fdd0",
          "type": "properties"
        }
      },
      "last_build": {
        "links": {
          "related": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a/last_build"
        },
        "data": {
          "id": "BL995725ac49104fd58eb1ea97416a7a47",
          "type": "builds"
        }
      }
    },
    "links": {
      "property": "https://reactor.adobe.io/properties/PR331f44fae6bf4d23a35656120fc9fdd0",
      "self": "https://reactor.adobe.io/libraries/LB3b9a06fb85dd4aa68b37980624a8334a"
    },
    "meta": {
      "build_status": "succeeded"
    }
  }
}

The “name” attribute in line 7 tells me all I want to know: the Library that someone (me) built here was called “Test”.

Notes

For our original use case — knowing about when people build Libraries — this is all we need to do.

It would make sense to be a bit more subtle, i.e. maybe only signal actual publishes. This can be done by watching lines 8 and 9 in the result.

And, when my extremely funky colleagues in the Launch teams have done their magic, maybe you won’t need any of the above.

I still think it is cool to be able to do this.

5 thoughts on “Using Audit Events in Launch

  1. Your timing couldn’t be any better Jan!! I’m just getting – or at least trying to get – started setting up this very thing to use for sending automated publish alerts to our Slack workspace. With my current workload, it may be a couple of weeks, but I’ll try to see if I can follow along with your use case and report back here when I’m done (or exhausted from trying… 😉 ). Thanks again!

    Like

  2. Hi Jan – missed seeing you at Summit this year.

    Your post gave me all sorts of ideas I can implement for my clients, especially those where we are trying to get some guardrails around who makes changes to Launch and if conventions and rules are being followed.

    Another use we have found for these Audit Events is using the production publish notification to trigger deploying the build to a self-host server. Next enhancement will be to auto-generate release notes by reading the contents of the published library.

    Like

    1. Hi Jim,

      I wasn’t supposed to go to Summit in Vegas this year, so for me Global Summit was a positive development amongst all the madness…

      Feel free to let us know what comes out of your ideas! I think Audit Events are still underrated!

      Be safe and sane,
      Jan

      Liked by 1 person

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.