Skip to content

Commit 0798be0

Browse files
authored
Merge pull request #10 from snowplow-incubator/enrichments
Add support for self-service enrichments
2 parents 2c0e039 + 54f1946 commit 0798be0

23 files changed

+395
-47
lines changed

app/app.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

app/requirements.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
FROM python:3.12
1+
FROM python:3.12-slim
22
WORKDIR /app
33
COPY requirements.txt .
44
RUN pip install -r requirements.txt
5-
COPY . .
5+
COPY app.py app.py

control-plane/app.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from base64 import b64decode, b64encode
2+
from glob import glob
3+
import json
4+
import logging
5+
6+
import docker
7+
import requests
8+
from flask import Flask, request
9+
from flask_cors import CORS
10+
11+
12+
app = Flask(__name__)
13+
CORS(app)
14+
logging.getLogger('flask_cors').level = logging.DEBUG
15+
16+
valid_containers = ['snowplow-stream-collector', 'snowplow-enrich', 'snowplow-iglu-server',
17+
'connect', 'snowflake-streaming-loader', 'snowflake-streaming-loader-incomplete',
18+
'lake-loader', 'bigquery-loader', 'snowbridge', 'ngrok-tunnel']
19+
20+
client = docker.from_env()
21+
22+
PATH = '../enrich/enrichments'
23+
## Container API
24+
25+
def restart(name: str):
26+
try:
27+
if name in valid_containers:
28+
container = client.containers.get(name)
29+
container.restart()
30+
return {'restart': True}
31+
else:
32+
return {'restart': False, 'error': 'Invalid container name'}
33+
except Exception as e:
34+
return {'restarted': False, 'exception': str(e)}
35+
36+
@app.route('/containers/restart')
37+
def restart_container():
38+
name = request.args.get('container')
39+
return restart(name)
40+
41+
@app.route('/containers/start')
42+
def start_container():
43+
name = request.args.get('container')
44+
try:
45+
if name in valid_containers:
46+
container = client.containers.get(name)
47+
container.start()
48+
return {'start': True}
49+
else:
50+
return {'start': False, 'error': 'Invalid container name'}
51+
except Exception as e:
52+
return {'start': False, 'exception': str(e)}
53+
54+
@app.route('/containers/list')
55+
def list_containers():
56+
containers = client.containers.list()
57+
output = [{'name': container.name, 'status': container.status} for container in containers if container.name in valid_containers]
58+
return output
59+
60+
@app.route('/containers/status')
61+
def container_status():
62+
name = request.args.get('container')
63+
try:
64+
container = client.containers.get(name)
65+
return {'status': container.status}
66+
except Exception as e:
67+
return {'status': 'error', 'exception': str(e)}
68+
69+
70+
## Enrichment API
71+
72+
## List enrichments
73+
@app.route('/pipelines/enrichments/', methods=['GET'])
74+
def read_enrichments():
75+
enrichments = []
76+
for filename in glob(f'{PATH}/*.json'):
77+
print(filename)
78+
with open(filename, 'r') as file:
79+
enrichments.append({
80+
**json.loads(file.read())
81+
})
82+
return enrichments
83+
84+
## Write single enrichment
85+
@app.route('/pipelines/enrichments/', methods=['POST'])
86+
def write_enrichment():
87+
data = request.json
88+
enrichment_name = data['data']['name']
89+
if enrichment_name == 'javascript_script_config':
90+
script = data['data']['parameters']['script'].encode()
91+
data['data']['parameters']['script'] = b64encode(script).decode('utf-8')
92+
93+
with open(f'{PATH}/{enrichment_name}.json', 'w') as file:
94+
file.write(json.dumps(data, indent=4))
95+
restart('snowplow-enrich')
96+
return {'success': True}
97+
98+

control-plane/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
flask==3.0.3
2+
flask-cors==5.0.0
3+
docker==7.1.0

docker-compose.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ services:
287287

288288
local-failed-ui:
289289
container_name: "local-failed-ui"
290-
image: "snowplow/failed-events-ui:0.0.1"
290+
image: "snowplow/failed-events-ui:0.0.2"
291291
ports:
292292
- "3001:3000"
293293

@@ -305,4 +305,17 @@ services:
305305
- "./tunnel/ngrok.yml:/etc/ngrok.yml"
306306
- "./tunnel/policy.yml:/etc/policy.yml"
307307
ports:
308-
- "4040:4040"
308+
- "4040:4040"
309+
310+
control-plane:
311+
container_name: "snowplow-control-plane"
312+
build: "control-plane/"
313+
command: "flask run --host=0.0.0.0 --debug"
314+
environment:
315+
- "FLASK_APP=app"
316+
volumes:
317+
- "./enrich:/enrich"
318+
- "/var/run/docker.sock:/var/run/docker.sock"
319+
ports:
320+
- "8083:5000"
321+

enrich/enrichments/anon_ip.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/anon_ip/jsonschema/1-0-0",
3+
"data": {
4+
"name": "anon_ip",
5+
"vendor": "com.snowplowanalytics.snowplow",
6+
"enabled": false,
7+
"parameters": {
8+
"anonOctets": 1
9+
}
10+
}
11+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow.enrichments/api_request_enrichment_config/jsonschema/1-0-0",
3+
"data": {
4+
"name": "api_request_enrichment_config",
5+
"vendor": "com.snowplowanalytics.snowplow.enrichments",
6+
"enabled": false,
7+
"parameters": {
8+
"inputs": [
9+
{
10+
"key": "user",
11+
"pojo": {
12+
"field": "user_id"
13+
}
14+
}
15+
],
16+
"api": {
17+
"http": {
18+
"method": "GET",
19+
"uri": "http://api.acme.com/users/{{client}}/{{user}}?format=json",
20+
"timeout": 5000,
21+
"authentication": {
22+
"httpBasic": {
23+
"username": "NA",
24+
"password": "NA"
25+
}
26+
}
27+
}
28+
},
29+
"outputs": [
30+
{
31+
"schema": "iglu:com.acme/user/jsonschema/1-0-0",
32+
"json": {
33+
"jsonPath": "$.record"
34+
}
35+
}
36+
],
37+
"cache": {
38+
"size": 3000,
39+
"ttl": 60
40+
}
41+
}
42+
}
43+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/campaign_attribution/jsonschema/1-0-0",
3+
"data": {
4+
"name": "campaign_attribution",
5+
"vendor": "com.snowplowanalytics.snowplow",
6+
"enabled": true,
7+
"parameters": {
8+
"mapping": "static",
9+
"fields": {
10+
"mktMedium": ["utm_medium"],
11+
"mktSource": ["utm_source"],
12+
"mktTerm": ["utm_term"],
13+
"mktContent": ["utm_content"],
14+
"mktCampaign": ["utm_campaign"]
15+
}
16+
}
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/cookie_extractor_config/jsonschema/1-0-0",
3+
"data": {
4+
"name": "cookie_extractor_config",
5+
"vendor": "com.snowplowanalytics.snowplow",
6+
"enabled": false,
7+
"parameters": {
8+
"cookies": []
9+
}
10+
}
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow.enrichments/cross_navigation_config/jsonschema/1-0-0",
3+
"data": {
4+
"enabled": false,
5+
"vendor": "com.snowplowanalytics.snowplow.enrichments",
6+
"name": "cross_navigation_config"
7+
}
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/currency_conversion_config/jsonschema/1-0-0",
3+
"data": {
4+
"enabled": false,
5+
"vendor": "com.snowplowanalytics.snowplow",
6+
"name": "currency_conversion_config",
7+
"parameters": {
8+
"accountType": "DEVELOPER",
9+
"apiKey": "PLACEHOLDER",
10+
"baseCurrency": "EUR",
11+
"rateAt": "EOD_PRIOR"
12+
}
13+
}
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"data": {
3+
"enabled": true,
4+
"name": "event_fingerprint_config",
5+
"parameters": {
6+
"excludeParameters": [
7+
"cv",
8+
"eid",
9+
"nuid",
10+
"stm"
11+
],
12+
"hashAlgorithm": "SHA1"
13+
},
14+
"vendor": "com.snowplowanalytics.snowplow"
15+
},
16+
"schema": "iglu:com.snowplowanalytics.snowplow/event_fingerprint_config/jsonschema/1-0-1"
17+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow.enrichments/http_header_extractor_config/jsonschema/1-0-0",
3+
"data": {
4+
"name": "http_header_extractor_config",
5+
"vendor": "com.snowplowanalytics.snowplow.enrichments",
6+
"enabled": false,
7+
"parameters": {
8+
"headersPattern": ".*"
9+
}
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow.enrichments/iab_spiders_and_robots_enrichment/jsonschema/1-0-0",
3+
"data": {
4+
"name": "iab_spiders_and_robots_enrichment",
5+
"vendor": "com.snowplowanalytics.snowplow.enrichments",
6+
"enabled": false,
7+
"parameters": {
8+
"ipFile": {
9+
"database": "ip_exclude_current_cidr.txt",
10+
"uri": "gs://bucket-name"
11+
},
12+
"excludeUseragentFile": {
13+
"database": "exclude_current.txt",
14+
"uri": "gs://bucket-name"
15+
},
16+
"includeUseragentFile": {
17+
"database": "include_current.txt",
18+
"uri": "gs://bucket-name"
19+
}
20+
}
21+
}
22+
}

enrich/enrichments/ip_lookups.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/ip_lookups/jsonschema/2-0-0",
3+
"data": {
4+
"name": "ip_lookups",
5+
"vendor": "com.snowplowanalytics.snowplow",
6+
"enabled": false,
7+
"parameters": {
8+
"geo": {
9+
"database": "GeoLite2-City.mmdb",
10+
"uri": "gs://max-mind-geolite2/"
11+
}
12+
}
13+
}
14+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"schema": "iglu:com.snowplowanalytics.snowplow/javascript_script_config/jsonschema/1-0-1",
3+
"data": {
4+
"vendor": "com.snowplowanalytics.snowplow",
5+
"name": "javascript_script_config",
6+
"parameters": {
7+
"script": "="
8+
},
9+
"enabled": false
10+
}
11+
}

0 commit comments

Comments
 (0)