Skip to content

Commit

Permalink
repo: Merge branch 'v3'
Browse files Browse the repository at this point in the history
  • Loading branch information
ivynya committed Aug 15, 2023
1 parent d2c30b3 commit b734d11
Show file tree
Hide file tree
Showing 32 changed files with 1,002 additions and 337 deletions.
15 changes: 15 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

# Changelog

### Analytics V3
1. Service now written in Go for resource efficiency and speed.
2. New `GET` `/v3/campaign/:id/interaction/:iID` endpoint to get campaign data for a specific interaction
3. JSON responses are now returned in streamlined Page schema
4. Deprecated V1 API - will continue to function but will be removed in a future release (see Migration Guide)

### Analytics V2
1. New endpoints and database schema to handle Interactions, a granular way to track campaign performance
2. New endpoint to get campaign data without creating a visit

### Analytics V1
1. Initial release
50 changes: 48 additions & 2 deletions .github/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,58 @@

# Analytics V1 to V2 Migration
# Migration Guide

This migration guide is a comprehensive guide to the changes you need to make in Notion and in your code to upgrade your analytics version.

## V2 -> V3 Migration

### In Notion
V3 uses the exact same database format as V2, so you can keep your existing Notion database and integration.

### In Code
You need to update any services calling the analytics API.
1. All V2 endpoints have a V3 endpoint that is functionally equivalent, so you can just update the endpoint from /v2/ to /v3/ as shown below.

| method | old endpoint | new endpoint |
|---|---|---|
| GET | `/v2/campaign/:id` | `/v3/campaign/:id` |
| POST | `/v2/campaign/:id` | `/v3/campaign/:id` |
| POST | `/v2/campaign/:id/interaction` | `/v3/campaign/:id/interaction` |
| POST | `/v2/campaign/:id/interaction/:interactionId` | `/v3/campaign/:id/interaction/:interactionId` |
| POST | `/v2/campaign/:id/visit/:interactionId` | `/v3/campaign/:id/visit/:interactionId` |

2. If you use JSON responses from the API (If `Public`=`True` for any of your campaigns), then you need to alter your code to use the new response schema as shown below. If you aren't using Public campaigns, then successes will continue returning 204 status codes and you can ignore this step.

| property name | type |
|---|---|
| `id` | `string` |
| `campaign_id` | `string` |
| `parent_campaigns` | `string[]` |
| `interact` | `string` |
| `public` | `string` |
| `ref_visits` | `number` |
| `visits` | `number` |
| `interactions` | `number` |

You can find the documentation on the rest of the new endpoints in the README.

---

## V1 -> V2 Migration

### In Notion
Using your existing Notion database:
1. Create an property of type `Number` named `Interactions`.
2. Create a property of type `Select` named `Interact` with the options `Enabled`, `Disabled`, and `Dynamic`.
3. Ensure you have a property called `CreatedBy` of type `Created by`.
4. Enable your database's `Subtasks` feature (using the `ParentCampaign` and `SubCampaigns` property names respectively) for better visual organization. Alternatively, just ensure the `ParentCampaign` of type `Relation` property exists.

Then, you can deploy your new Analytics instance (example instructions in README) and start using the new APIs, documented there as well.
### In Code
The only API endpoint in V1 is forward-compatible with V2, so you can just update the endpoint from /v1/ to /v2/ as shown below.

| method | old endpoint | new endpoint |
|---|---|---|
| POST | `/v1/campaign/:id` | `/v2/campaign/:id` |

You can find the documentation on the rest of the new endpoints in the README.

Your new Analytics instance and database format is still compatible with your `redirect` instance, if you are running the two alongside each other.
92 changes: 62 additions & 30 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# analytics
Notion-integrated analytics API with interaction tracking. Features user-definable and app-controlled management of KPIs.

> ⚠️ Are you an Analytics V1 user? See the [V2 migration guide](./MIGRATION.md) to see how you can preserve your existing data and make use of the new Interactions API and App-Controlled Campaigns. The V1 api will continue to function as-is.
![https://img.shields.io/github/v/release/ivynya/analytics?label=version](https://img.shields.io/github/v/release/ivynya/analytics?label=version)

## Using the Notion Template
Notion-integrated analytics solution with interaction tracking. Features user-definable and app-controlled management of different campaigns and KPIs by API and visually through a Notion database.

> ⚠️ Are you an Analytics V1 or V2 user? See the [migration guide](./MIGRATION.md) to see how you can preserve your existing data and make use of new features and performance improvements in V3. No rush, though! V1 and V2 API endpoints are still supported and functional for now.
To get started, duplicate this Notion page template: [ivy.direct/template/analytics/v2](https://ivy.direct/template/analytics/v2)
## Using the Notion Template

> ⚠️ The Analytics V2 template is compatible with Analytics V1 and is the recommended default. However, if you only want V1 features, you can use the old template: [ivy.direct/template/analytics/v1](https://ivy.direct/template/analytics/v1)
To get started, duplicate this Notion page template: [ivy.direct/template/analytics/v3](https://ivy.direct/template/analytics/v3)

[![Notion Template](https://github.com/ivynya/analytics/raw/main/.github/v2_template.jpg)](https://ivy.direct/template/analytics/v2)
[![Notion Template](./v3_template.jpg)](https://ivy.direct/template/analytics/v3)

Do not edit or delete any of the property names as the API requires these to function. You can add additional properties for organizational purposes, or create views that hide the properties you don't need instead.

### API V2 Property Defintions
### V3 Notion Property Defintions
| Property | Description |
| --- | --- |
| `Campaign` | A friendly name for the campaign. Can be anything. |
Expand All @@ -28,31 +29,60 @@ Do not edit or delete any of the property names as the API requires these to fun
| `SubCampaigns` (hidden by default) | Any associated sub-campaigns. Not used by the API directly. |
| `CreatedBy` (hidden by default) | Shows who created this campaign (you, another user, or the Analytics API) |

## Hosting the API

### With Deno Deploy
1. [Create a new Notion integration](https://www.notion.so/my-integrations) with all permissions, copy the API token, and invite it to your duplicated Notion page. If you don't want to use the App-Controlled Campaigns feature, you can safely leave out the Insert Content permission.
2. Fork this repository's `/deployable` branch and create a new automatic [Deno Deploy](https://deno.com/deploy) instance from it, adding the `NOTION_TOKEN` (from your integration) and `NOTION_DB_ID` (from your duplicated Notion page's ID: see [Notion's guide on how to find this](https://developers.notion.com/docs/create-a-notion-integration#step-3-save-the-database-id)) as environment variables
3. Make a `POST` request to `https://YOUR-SUBDOMAIN-HERE.deno.dev/v2/campaign/portfolio-github` to see it work.
## Hosting the Analytics API
1. [Create a new Notion integration](https://www.notion.so/my-integrations) with all permissions, copy the API token, and invite it to your duplicated Notion page.
2. Pull the Docker image artifact from GitHub Container Registry
3. Clone this repo and create a `.env` file with the ID and token, according to `.env.example`
4. Run the Docker container and pass in the environment variables: `docker run -p 3000:3000 --env-file .env ivynya/analytics`
5. Make a `POST` request to `http://localhost:3000/v3/campaign/portfolio-github` to see it work.

### As Docker Container
1. [Create a new Notion integration](https://www.notion.so/my-integrations) with all permissions, copy the API token, and invite it to your duplicated Notion page. If you don't want to use the App-Controlled Campaigns feature, you can safely leave out the Insert Content permission.
2. Clone this repo and create a `.env` file with the ID and token, according to `.env.example`
3. Run `docker build -t analytics .` and `docker run -p 8000:8000 -d analytics`
4. Make a `POST` request to `http://localhost:8000/v2/campaign/portfolio-github` to see it work.
5. If you don't want to build the container with your secrets, you can also use Docker environment flags to pass these values in at runtime.
## V3 API - Endpoints
The following API endpoints are described in the table below. Each endpoint may return one of the listed HTTP status codes in the `Returns` column, as well as the additional possibility of a `200 OK` (described by the Standard Response Protocol further below) or a `500` error if things go catastrophically wrong.

## API Usage
| Endpoint | Description | Returns |
| --- | --- | --- |
| `GET /v2/campaign/:CampaignID` | Gets campaign info as JSON response. | `200` or `400` if campaign not found or Public = false |
| `POST /v2/campaign/:CampaignID` | Registers +1 Visit. If the campaign is a sub-campaign, the parent will also be updated with +1 RefVisit and +1 Visit. | `204` or `400` if campaign not found |
| `POST /v2/campaign/:CampaignID /interact` | Registers +1 Interaction. If the campaign is a sub-campaign, the parent will also be updated with +1 Interaction. | `204` or `400` if campaign not found or Interact = Disabled |
| `POST /v2/campaign/:CampaignID /interact/:InteractionID` | Creates a sub-campaign for :CampaignID with default values. If exists already, registers +1 interaction. | `204` or `400` if campaign not found |
| `POST /v2/campaign/:CampaignID /visit/:InteractionID` | Creates a sub-campaign for :CampaignID with default values. If exists already, registers +1 visit. | `204` or `400` if campaign not found |
| `GET /v3/campaign/:CampaignID` | Gets campaign info as JSON response. | `200`, `204`, `400` SRP(Campaign) |
| `POST /v3/campaign/:CampaignID` | Registers +1 Visit. If the campaign is a sub-campaign, the parent will also be updated with +1 RefVisit and +1 Visit. | `200`, `204`, `400` SRP(Campaign) |
| `POST /v3/campaign/:CampaignID /interaction` | Registers +1 Interaction. If the campaign is a sub-campaign, the parent will also be updated with +1 Interaction. | `200`, `204`, `400` SRP(Campaign) |
| `GET /v3/campaign/:CampaignID /interaction/:InteractionID` | Gets interaction info as JSON response. | `200`, `204`, `400` SRP(Subcampaign) |
| `POST /v3/campaign/:CampaignID /interact/:InteractionID` | Creates a sub-campaign for :CampaignID with default values. If exists already, registers +1 interaction. | `200`, `204`, `400` SRP(Subcampaign) |
| `POST /v3/campaign/:CampaignID /visit/:InteractionID` | Creates a sub-campaign for :CampaignID with default values. If exists already, registers +1 visit. | `200`, `204`, `400` SRP(Subcampaign) |

After Analytics makes an edit, you can see a summary of all unread changes in the Notion history for the page.

## V3 API - Standard Response Protocol (SRP)

The standard response protocol is a flow that describes potential responses from the API if the given Campaign or Interaction is set to `Public` = `True`or not in the Notion database.

For **SRP(Campaign)** endpoints described above, if `Public` is `True` and the API request is successful, the endpoint will return a `200 OK` response with the following JSON schema describing the `Campaign` that corresponds to the `:CampaignID` called:

After Analytics makes an edit, you can see a summary of all unread changes as a Notion update (if you follow the page, which is true by default):
[![Notion Update](./v2_example.jpg)](https://ivy.direct/template-analytics)
```ts
{
"ID": string // Notion ID of the campaign
"CampaignID": string, // User-defined ID of the campaign
"Visits": number, // Total visits to the campaign
"RefVisits": number, // Total visits to the campaign from sub-campaigns
"Interactions": number, // Total interactions with the campaign
"Public": string, // "True" or "False"
"Interact": "Dynamic", // "Dynamic" "Enabled" or "Disabled" - Dynamic allows API requests to create sub-campaigns to track interactions, Enabled allows API requests to track interactions, Disabled does not allow API requests to track interactions at all
}
```

If the campaign is not public (`Public` = `False`), and the API request was otherwise successful, the API will return a `204 No Content` response.

For **SRP(Interaction)** endpoints described in the table above, the exact same response flow and JSON schema are used - except, instead of describing `:CampaignID`, it describes the campaign object represented by `:CampaignID-:InteractionID` (`:InteractionID` subcampaign of `:CampaignID`) instead.

## API Support Matrix

The following table shows which Analytics release supports which API versions. Use this table to inform your decision on whether or not you can upgrade to a newer version of Analytics while still using old API versions.

**❌ = Not Supported | ✅ = Supported | ⚠️ = Deprecated, Functional**

| API Version | Analytics V1 | Analytics V2 | Analytics V3 |
| --- | --- | --- | --- |
| V1 ||| ⚠️ |
| V2 ||| ⚠️ |
| V3 ||||

## Suggested Usage
I use top-level campaigns to track a project as a whole (total visits, referrals to it, and interactions on that project) in combination with my [redirect](https://github.com/ivynya/redirect) service.
Expand All @@ -64,9 +94,11 @@ Finally, for items that require dynamic tracking, I use the `Interact` property
These analytics hits are typically done server-side to prevent being blocked by scripts or slowing down page loads. Because no "creepy" or personally-identifiable data (that I wouldn't be able to use anyway) like location, IP, device specifications, mouse cursor movement, etc etc is collected - only page hit numbers and interactions are - this is a great way to track user behavior without being invasive.

## Compatibility with [ivynya/redirect](https://github.com/ivynya/redirect)
Analytics V2 remains compatible with `redirect` to track visits for dynamic redirects, managed from Notion. See the `redirect` GitHub page for setup and usage.
Analytics V3 remains compatible with `redirect` to track visits for dynamic redirects, managed from Notion. See the `redirect` GitHub page for setup and usage.

## Licensing & Contributing
Contributions are welcome! Please first open an issue on this repository.
Contributions are welcome! Please first open an issue on this repository before making a pull request.

To run the project locally, you need Go installed (tested on 1.20+). Create a .env file according to the .env.example and then source it into your environment. Then, run `go run ./cmd/analytics` to start the server on localhost:3000.

This repository lives under MIT license.
This repository lives under MIT license.
Binary file removed .github/v2_example.jpg
Binary file not shown.
Binary file added .github/v3_template.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Build and Push Docker Image to GHCR

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}/analytics:latest

3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@

.env
notion/cache.json
.env
8 changes: 4 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"deno.enable": true,
"deno.suggest.imports.hosts": {
"https://deno.land": true
}
"go.toolsEnvVars": {
"GO111MODULE": "on"
},
"dotenv.enableAutocloaking": false
}
20 changes: 10 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

FROM denoland/deno:alpine-1.23.1

EXPOSE 8000

# Use the official Golang image as the base image
FROM golang:1.21-alpine as builder
WORKDIR /app

COPY deps.ts .
COPY . .
RUN go build ./cmd/analytics

RUN deno cache deps.ts
RUN deno cache main.ts
# Use slim alpine image for production
FROM alpine:3.18 as production
WORKDIR /app
COPY --from=builder /app/analytics .
EXPOSE 3000

CMD ["run", "-A", "main.ts"]
# Run the Go program when the container starts
CMD ["./analytics"]
35 changes: 0 additions & 35 deletions api/v1.ts

This file was deleted.

Loading

1 comment on commit b734d11

@deno-deploy
Copy link

@deno-deploy deno-deploy bot commented on b734d11 Aug 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed to deploy:

Module not found "file:///src/main.ts".

Please sign in to comment.