Skip to content

Add API endpoint to proxy requests to Cube installation

Max Woolf requested to merge mw/generate-cube-api-tokens into master

What does this MR do and why?

  • Adds a new API endpoint: POST /api/v4/projects/6/product_analytics/request to pass a valid request for the current user and project to Cube
  • Adds a new application setting to store the secret value required to sign the tokens to validate their origin and the base API value.

Overview

  • This is the first MR that groupproduct analytics is adding to the main GitLab codebase. It allows for users with developer+ access to a project to connect to a Cube API endpoint.
  • This API endpoint proxies requests to the cube /v1/load endpoint. (Docs) which allows for querying project analytics datasets. By proxying the requests in the backend we can generate the required JWT internally as only symmetric encryption is supported by cube. Alternatively we would have to expose that key to the frontend or provide an API endpoint to generate JWTs only. (We considered this before and moved to fully proxy the requests instead.)
  • An example query has been provided below.

It is expected that when the user visits a page that will display product analytics data, the frontend will call the product_analytics/request endpoint with the required request. This will proxy the request to cube, which will respond with the data as structured JSON. We will then draw the graphs using vue components.

Database review

Migrate

main: == 20220825105631 AddCubeApiKeyToApplicationSettings: migrating ===============
main: -- add_column(:application_settings, :cube_api_base_url, :text)
main:    -> 0.0012s
main: -- add_column(:application_settings, :encrypted_cube_api_key, :binary)
main:    -> 0.0007s
main: -- add_column(:application_settings, :encrypted_cube_api_key_iv, :binary)
main:    -> 0.0005s
main: == 20220825105631 AddCubeApiKeyToApplicationSettings: migrated (0.0026s) ======

main: == 20220830082928 AddTextLimitToCubeApiBaseUrl: migrating =====================
main: -- transaction_open?()
main:    -> 0.0000s
main: -- current_schema()
main:    -> 0.0001s
main: -- transaction_open?()
main:    -> 0.0000s
main: -- execute("ALTER TABLE application_settings\nADD CONSTRAINT check_8e7df605a1\nCHECK ( char_length(cube_api_base_url) <= 512 )\nNOT VALID;\n")
main:    -> 0.0021s
main: -- current_schema()
main:    -> 0.0001s
main: -- execute("SET statement_timeout TO 0")
main:    -> 0.0005s
main: -- execute("ALTER TABLE application_settings VALIDATE CONSTRAINT check_8e7df605a1;")
main:    -> 0.0008s
main: -- execute("RESET statement_timeout")
main:    -> 0.0001s
main: == 20220830082928 AddTextLimitToCubeApiBaseUrl: migrated (0.0082s) ============

Rollback

main: == 20220830082928 AddTextLimitToCubeApiBaseUrl: reverting =====================
main: -- transaction_open?()
main:    -> 0.0000s
main: -- transaction_open?()
main:    -> 0.0000s
main: -- execute("ALTER TABLE application_settings\nDROP CONSTRAINT IF EXISTS check_8e7df605a1\n")
main:    -> 0.0011s
main: == 20220830082928 AddTextLimitToCubeApiBaseUrl: reverted (0.0072s) ============

main: == 20220825105631 AddCubeApiKeyToApplicationSettings: reverting ===============
main: -- remove_column(:application_settings, :encrypted_cube_api_key_iv, :binary)
main:    -> 0.0007s
main: -- remove_column(:application_settings, :encrypted_cube_api_key, :binary)
main:    -> 0.0007s
main: -- remove_column(:application_settings, :cube_api_base_url, :text)
main:    -> 0.0003s
main: == 20220825105631 AddCubeApiKeyToApplicationSettings: reverted (0.0029s) ======

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

How to set up and validate locally

  1. Enable the feature flag

    Feature.enable(:cube_api_proxy)
  2. Run the DB migrations.

  3. Update application settings: ApplicationSetting.current.update(cube_api_base_url: 'http://localhost:4000', cube_api_key: API KEY FROM CUBE .env)

  4. Restart GDK

  5. As a an authenticated user who has developer+ access to a particular project, make the following API call:

    POST /api/v4/projects/{PROJECT_ID}/product_analytics/request
    
    Content-Type: application/json

    Send it with a valid cube query as a JSON body:

    {
"query": {
  "measures": [
    "Jitsu.count"
  ],
  "timeDimensions": [
    {
      "dimension": "Jitsu.utcTime",
      "dateRange": "This week"
    }
  ],
  "order": [
    [
      "Jitsu.count",
      "desc"
    ],
    [
      "Jitsu.docPath",
      "desc"
    ],
    [
      "Jitsu.utcTime",
      "asc"
    ]
  ],
  "dimensions": [
    "Jitsu.docPath"
  ],
  "limit": 23
  }
}

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Max Woolf

Merge request reports