## Tracking - Controller Actions; Convention for usage

**Quick-start:** See [bc8d29f](../../../../../../../../admin-services/admin-panel/commit/bc8d29f953e9579941718ee42dad277e935988a4) for a basic real-world example of tracking.

### Definition

An area's tracking events are defined within the base controller of the area being tracked.

For example, to track an action within `PurchaseOrdersController`, first define your tracking event within the payments base controller ( as `class PurchaseOrdersController < Payments::BaseController` )
The result should look something like the following:

```ruby
TrackedEvent::Scaffold.class_eval do
    # rubocop:disable ParameterLists, TrailingWhitespace

    #
    # @param [String] purchaser_login the twitch login of the purchaser
    # @param [String] recipient_login the twitch login of the user receiving the benefit
    # @param [String] ext_subscription_id the ID of a subscription in the payment provider's system
    # @param [String] product_short_name the product's short_name,
    #        e.g.,    "twitchsupport", "twitchsupport_3000"
    # @param [String] product_owner_login the twitch login of the user that owns the product
    # @param [String] product_type the product type,
    #        e.g.,    "Bits", "Channel Sub", "Gift Sub", "Sub Token"
    # @param [String] purchase_platform the platform the purchase was made on,
    #        i.e.,    "Web", "Android", "iOS"
    # @param [String] payment_provider the payment provider used for the purchase,
    #        i.e.,    "Recurly", "Xsolla", "Zuora", "Google IAB", "Apple IAP", "Sub Token"
    # @param [String] purchase_order_state the purchase order's success state,
    #        i.e.,    "Success", "Failed"
    # @param [String] created_after_utc the search filter's UTC start date
    # @param [String] created_before_utc the search filter's UTC end date
    # @param [String] ext_transaction_id the ID of a transaction in the payment provider's system
    # @param [String] purchase_order_id the ID of a Purchase Order in Twitch's system
    # @param [String] origin_id the object ID of the Purchase Order
    #
    # @return (see TrackedEvent::Scaffold#build)
    #
    # @macro eventer
    def admin_panel_purchase_orders_search(
      purchaser_login:,
      recipient_login:,
      ext_subscription_id:,
      product_short_name:,
      product_owner_login:,
      product_type:,
      purchase_platform:,
      payment_provider:,
      purchase_order_state:,
      created_after_utc:,
      created_before_utc:,
      ext_transaction_id:,
      purchase_order_id:,
      origin_id:
    )
      build(context: binding)
    end
    # rubocop:enable ParameterLists, TrailingWhitespace
  end
```

We're leveraging Keyword arguments to auto-generate fields within the main `TrackedEvent::Scaffold`, so the only thing a tracking method defined in this way _needs_ is keyword argument parameters, and a single `build(context: binding)` line ( which calls the class's internal build method, passing the current execution context binding so it can fetch params & values )

**note:** rubocop command directives are only added temporarily, until we can calm rubocop down at a project level; additionally, while you technically don't have to document your tracking event's fields, you should for other readers.

### Usage

Track your action using the designated helper methods, in combination with your own data - either directly at the end of a method, or with a helper method ( like so ):

```ruby
    def track_purchase_orders_search!(params:)
      track_action! trackable_request.assemble_an.admin_panel_purchase_orders_search(
        purchaser_login:      params[:purchaser_login],
        recipient_login:      params[:recipient_login],
        ext_subscription_id:  params[:ext_subscription_id],
        product_short_name:   params[:product_short_name],
        product_owner_login:  params[:product_channel_login],
        product_type:         params[:product_type],
        purchase_platform:    params[:platform],
        payment_provider:     params[:payment_gateway],
        purchase_order_state: params[:state],
        created_after_utc:    params[:created_at_start],
        created_before_utc:   params[:created_at_end],
        ext_transaction_id:   params[:ext_transaction_id],
        purchase_order_id:    params[:purchase_order_id],
        origin_id:            params[:origin_id]
      )
      return
    end
```

```ruby
   def index
      ...
      track_purchase_orders_search!(params: params)
    end
```

Any fields that satisfy `empty?` or `nil?` will be filtered from the final tracking event, so you don't need to worry about polluting fields in that manner. Blank spaces, however, will still be accepted as valid.

---

### Provided facilities

#### in `ApplicationController`

`trackable_request` - provides a `TrackedEvent::Scaffold` with the current Request's current user `ldap`, `user_agent`, `client_ip`, `page_url`, and `session_id` already set.

`track_action!(event)` - concatenates existing pending tracking events (if any) with the incoming tracking event

#### in `TrackedEvent::Scaffold`

`trackable_request`.`assemble_an` - grammatical helper for ease of reading when combined with child methods defining tracking events

#### in RSpec

`include_context 'it creates any tracking call'` - provides a shared context for spec inclusion which encapsulates the expectation that _some_ tracking event will be fired, without additional expectations toward its field format.

---

**note:** Ideally, specific tests for specific events should still be made, as the shared examples do not check _format_, simply _existence_ of a call through expected channels. It's definitely possible to misuse those guarantees, as any tracking call with validate that claim even if it was not the tracking event you intended your test to validate. This is especially important to take into account when firing multiple events per action.
