01 — Architecture Assessment
This document captures the current state of the Adventive Looker Studio connector as of the April 2026 audit: module structure, data flow, API contract, and a complete register of every issue found. Use this as the baseline before applying any changes from §02.
Module inventory
Section titled “Module inventory”| File | Purpose | Lines |
|---|---|---|
appscript.json | Connector manifest — metadata, auth type, OAuth scopes, exception logging, runtime version | 35 |
Code.gs | Main connector logic — config, schema, data fetching, cache coordination, field mapping | 393 |
Auth.gs | Authentication module — getAuthType, setCredentials, isAuthValid, resetAuth, credential validation | 94 |
DataCache.gs | Chunked cache utility — splits responses > 100 KB across multiple CacheService keys | 86 |
Total: 608 lines across 4 files. Lightweight and appropriately scoped for a read-only analytics connector.
Runtime
Section titled “Runtime”The connector runs on the Google Apps Script V8 runtime. The manifest explicitly pins "runtimeVersion": "V8". Google retired the legacy Rhino runtime on 2026-01-31; any project without "runtimeVersion": "V8" in its manifest after that date stops executing. See §05 — V8 Runtime Migration for the CSV-audit triage procedure and the code-incompatibility review that scores this codebase against the Rhino → V8 delta.
Architecture
Section titled “Architecture”┌──────────────────────────────────────────────┐│ Google Looker Studio (browser) ││ User configures connector, builds reports │└──────────────────┬───────────────────────────┘ │ Calls connector functions┌──────────────────▼───────────────────────────┐│ Google Apps Script Runtime ││ ││ Auth.gs Code.gs DataCache.gs ││ ───────── ──────── ─────────── ││ getAuthType() getConfig() DataCache() ││ setCredentials() getSchema() .get() ││ isAuthValid() getData() .set() ││ resetAuth() fetchData() ││ fetchDataFromApi() ││ getColumns() ││ ││ PropertiesService CacheService ││ (credential store) (response cache) │└──────────────────┬───────────────────────────┘ │ HTTPS┌──────────────────▼───────────────────────────┐│ Adventive Analytics API v2.0 ││ https://api.adventive.com/v2.0/ ││ ││ /credentialscheck.json (auth validation) ││ /dataconnector.json (metrics data) │└──────────────────────────────────────────────┘Data flow
Section titled “Data flow”Authentication setup (one-time per user)
Section titled “Authentication setup (one-time per user)”User enters Integration Key + API Key in Looker Studio UI → Auth.setCredentials(request) → Auth.validateCredentials(username, password) → POST https://api.adventive.com/v2.0/credentialscheck.json Headers: X-Integration-Key, X-Api-Key Response 200 → valid Response 401 → invalid → return INVALID_CREDENTIALS error code → PropertiesService.getUserProperties().setProperty('dscc.username', ...) → PropertiesService.getUserProperties().setProperty('dscc.password', ...)Report data fetch (every time a chart renders or refreshes)
Section titled “Report data fetch (every time a chart renders or refreshes)”Looker Studio calls getData(request) → Read credentials from PropertiesService → Extract dateRange.startDate, dateRange.endDate from request → Build DataCache with key: intKey + '_' + startDate + '_' + endDate → fetchDataFromCache(cache) CacheService.getUserCache().get(chunkKey_0, chunkKey_1, ...) Hit → JSON.parse(cachedString) → content[] Miss → fetchDataFromApi(intKey, apiKey, startDate, endDate) GET https://api.adventive.com/v2.0/dataconnector.json ?data_connector=true&removeZeros=false&from=STARTDATE&to=ENDDATE Headers: X-Integration-Key, X-Api-Key → putDataInCache(content, cache) Splits JSON string into 100 KB chunks CacheService.put(chunkKey_n, chunk, 1800) → getColumns(content, requestedFields) Maps each API row to the ordered list of requested field IDs Returns [{values: [...]}, ...] → Return {schema: [...], rows: [...]} to Looker StudioAPI contract
Section titled “API contract”Credential validation endpoint
Section titled “Credential validation endpoint”GET https://api.adventive.com/v2.0/credentialscheck.jsonHeaders: X-Integration-Key: <integration_key> X-Api-Key: <api_key>Response: 200 OK → credentials valid 401 Unauthorized → credentials invalidData endpoint
Section titled “Data endpoint”GET https://api.adventive.com/v2.0/dataconnector.json ?data_connector=true &removeZeros=false &from=YYYYMMDD &to=YYYYMMDDHeaders: X-Integration-Key: <integration_key> X-Api-Key: <api_key>Response: JSON array of row objects, each keyed by field IDField schema (28 fields)
Section titled “Field schema (28 fields)”| ID | Display name | Type | Group | Notes |
|---|---|---|---|---|
an | Advertiser | DIMENSION / TEXT | Advertiser | |
cn | Campaign | DIMENSION / TEXT | Campaign | Default dimension |
sn | Site | DIMENSION / TEXT | Campaign | |
pn | Placement | DIMENSION / TEXT | Campaign | |
adn | Ad | DIMENSION / TEXT | Campaign | |
ymd | Date | DIMENSION / YEAR_MONTH_DAY | — | |
i | Impressions | METRIC / NUMBER | Analytics | Default metric |
iu | Unique Impressions | METRIC / NUMBER | Analytics | |
c | Clicks | METRIC / NUMBER | Analytics | |
cu | Unique Clicks | METRIC / NUMBER | Analytics | |
ctr | CTR % | METRIC / PERCENT | Analytics | Formula: SUM($c) / SUM($i) — calculated in Looker Studio |
e | Engagements | METRIC / NUMBER | Analytics | |
eu | Unique Engagements | METRIC / NUMBER | Analytics | |
er | ER % | METRIC / PERCENT | Analytics | Formula: SUM($eu) / SUM($i) — calculated in Looker Studio |
v0 | Video Plays | METRIC / NUMBER | Analytics | |
v1 | Video 25% | METRIC / NUMBER | Analytics | |
v2 | Video 50% | METRIC / NUMBER | Analytics | |
v3 | Video 75% | METRIC / NUMBER | Analytics | |
v4 | Video Complete | METRIC / NUMBER | Analytics | |
vcr | VCR % | METRIC / PERCENT | Analytics | Formula: SUM($v4) / SUM($v0) — calculated in Looker Studio |
CTR, ER, and VCR are declared as formula fields in the schema. The API does not return these values — Looker Studio computes them client-side from the raw counts. This is intentional (reduces API response size).
Credential storage
Section titled “Credential storage”| Property key | Value | Scope |
|---|---|---|
dscc.username | Adventive Integration Key | User properties (per-user, isolated) |
dscc.password | Adventive API Key | User properties (per-user, isolated) |
PropertiesService.getUserProperties() provides per-user isolation — one user’s credentials are never visible to another. There is no encryption layer beyond Apps Script’s built-in access controls.
Issues register — April 2026 audit
Section titled “Issues register — April 2026 audit”| # | Issue | Severity | Location | Category |
|---|---|---|---|---|
| 1 | fetchDataFromApi returns the HTTPResponse object; caller passes it directly to JSON.parse(). Every non-cached data fetch crashes with a SyntaxError. | Critical | Code.gs:312 | Bug |
| 2 | Integration Key (cacheToken) is written to Cloud Logging on every data request. | High | Code.gs:253 | Security |
| 3 | UrlFetchApp.fetch() in validateCredentials has no muteHttpExceptions. A 401 from the API throws an uncaught exception instead of returning false. | High | Auth.gs:50 | Standard compliance |
| 4 | UrlFetchApp.fetch() in fetchDataFromApi has no muteHttpExceptions. Any non-2xx response surfaces as an unhandled exception. | High | Code.gs:302 | Standard compliance |
| 5 | exceptionLogging is "STACKDRIVER" — deprecated name for Cloud Logging. | Medium | appscript.json | Standard compliance |
| 6 | All user-visible text says “Data Studio”. Google rebranded to “Looker Studio” in October 2022. | Medium | appscript.json, Code.gs | Standard compliance |
| 7 | Cache key does not include the requested field list. A cached response for fields A+B is returned for a subsequent request for fields A+B+C — the extra field silently returns empty strings. | Medium | DataCache.gs:30 | Bug |
| 8 | validateCredentials null-checks credentials but not empty string. An empty Integration Key passes validation and makes an invalid API request. | Medium | Auth.gs:38 | Bug |
| 9 | API endpoint URLs are assigned as properties on the cc (DataStudioApp) object — mutating a third-party global. | Low | Code.gs:23-24 | Code quality |
| 10 | No timeout on UrlFetchApp.fetch() calls. Connector can hang for up to 30 seconds on a slow API response. | Low | Code.gs:302, Auth.gs:50 | Code quality |
| 11 | throwConnectorError re-instantiates DataStudioApp.createCommunityConnector() instead of using the module-level cc. | Low | Code.gs:385 | Code quality |
| 12 | DataCache.REQUEST_CACHING_TIME comment says “6 hours, Google’s max” but the value is 1800 seconds (30 minutes). | Low | DataCache.gs:18-20 | Code quality |
| 13 | getColumns uses for (var i in array) to iterate numeric arrays. Harmless today with 1–2 elements, but for…in over arrays is a V8 foot-gun and blocks any future refactor that adds non-numeric properties. | Low | Code.gs:371, 384 | V8 hygiene |
| 14 | appscript.json lacks an explicit runtimeVersion: "V8". If the project was created before 2020 it may still be on Rhino — which retires 2026-01-31. | Critical | appscript.json | Runtime |
All 14 issues are addressed in §02 — Code Updates. The V8-runtime-specific triage and migration procedure are in §05 — V8 Runtime Migration. The rewritten files are in connector-rewrite/ (browse on GitHub).
Next: 02 — Code Updates