Skip to content

Commit d508fa8

Browse files
fix: Hack App Wholly Artifact Refactor (#1210)
- The biggest issue, was business logic relied on Hacker's `id` field which was the Looker User Id. Wholly Artifact required Id with `TABLE_NAME:` prefixed. We needed to maintain all relationships between other tables to the User table. Added userRecord field to the Hacker class and made it's `id` field refer to the User record's key. - Refactored hacker list to keep showing Looker Id instead of actual User Id. SUPER HACKY. Needs to be fixed later. Hack is commented in the code. - On getHacker() all hackers are loaded and initialized (with their associated user record). - If User record doesn't exist will be created/saved. So first call to getHacker after multiple users are added to looker instance will take a long while saving all the new user records. Possibly setup admin add looker user functionality to also create user records in the future. - Moved registerUser logic from sheetData to sheets_client - Updated most references from whollysheet to wholly artifact. However kept some wholly sheet references so tests can past lint/build. - Transitioned from Tech Ids being shown in the UI, to technology descriptions. (Project's `technologies` field should be `tech_ids`, and Project's `$techs` should be `$tech_names`, anddd Technology's `description` should be `name` or we should add a `name` field) <-Refactor for another time. Project class also now have technology names so that they're displayed correctly in the table list. - Current initial load of data is around 10 round trips or so. Super slow. Need to update to load all data in one roundtrip and then process the data into rows and put them in the models/Whollyartifacts. - Included a python notebook to help migrate data from sheets to artifact api. - Added another utility script for populating hackathon test data - Updated readme and added manifest.lkml file - Made Wholly Artifact's RowModel URI encodes string type values before they are json.stringify-ed and stored in artifact. Also URI decodes string type values after json.parse-ing artifact. JSON.parse errors on problem characters like '>'. So we URI encode/decode them. Co-authored-by: John Kaster <[email protected]>
1 parent 0d9d45d commit d508fa8

27 files changed

+806
-333
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,7 @@ csharp/*.cache
7676
# apix indexes
7777
declarationsIndex.json
7878
/apix-files/yarn.lock
79+
80+
# hackathon test files
81+
/hack*.json
82+

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"mine": "run-p -c mine:*",
2929
"mine:declarations": "ts-node -O '{ \"module\": \"commonjs\", \"target\": \"es2019\" }' packages/sdk-codegen-scripts/scripts/mineDeclarations.ts",
3030
"mine:examples": "ts-node -O '{ \"module\": \"commonjs\", \"target\": \"es2019\" }' packages/sdk-codegen-scripts/scripts/mineExamples.ts",
31+
"vox": "ts-node -O '{ \"module\": \"commonjs\", \"target\": \"es2019\" }' packages/sdk-codegen-scripts/scripts/vox.ts",
3132
"example": "ts-node -O '{ \"module\": \"commonjs\", \"target\": \"es2019\" }'",
3233
"view": "yarn api-explorer",
3334
"wipe": "rm -rf api spec",

packages/hackathon/README.md

+2-80
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,12 @@
11
# Hackathon extension
22

3-
This extension uses the `@looker/wholly-sheet` package (currently only available in this repository) for CRUDF operations on a GSheet.
4-
5-
Modifications of a GSheet cannot be done with an API key, so you must use a service account or OAuth.
6-
7-
Fear not! You can configure and use the [access token server](/examples/access-token-server) to get your service account token to use with this extension.
8-
93
## Configuration
104

115
There are some items that need to be configured for the Hackathon extension to function.
126

13-
### Google Sheet
14-
15-
To configure your GSheet, see the instructions in the [WhollySheet readme](../wholly-sheet/README.md#getting-your-gsheet-credentials)
16-
17-
To configure your access token server, see the instructions in the [access token server example readme](../../examples/access-token-server/README.md)
18-
197
### Hackathon extension
208

21-
Until the Hackathon extension is available via the Looker Marketplace, a Hackathon manifest needs to be used. This manifest should have:
22-
23-
```lookml
24-
project_name: "hackathon"
25-
application: hackathon {
26-
label: "Hackathon"
27-
url: "https://localhost:8080/dist/bundle.js"
28-
# file: "bundle.js"
29-
entitlements: {
30-
local_storage: no
31-
navigation: yes
32-
new_window: yes
33-
new_window_external_urls: [
34-
"https://*.looker.com/*",
35-
"https://*.google.com/*",
36-
"https://*.bit.ly/*",
37-
"https://*.imgur.com/*",
38-
"https://*.slack.com/*",
39-
"https://*.github.com/*",
40-
"https://*.youtube.com/*",
41-
"https://*.youtu.be/*",
42-
"https://*.vimeo.com/*"
43-
]
44-
use_form_submit: yes
45-
use_embeds: no
46-
use_iframes: no
47-
use_clipboard: no
48-
external_api_urls: ["http://localhost:8081/*", "https://sheets.googleapis.com/*"]
49-
core_api_methods: [
50-
"me",
51-
"all_roles",
52-
"all_user_attributes",
53-
"delete_user_attribute",
54-
"create_user_attribute",
55-
"search_groups",
56-
"search_users",
57-
"user_roles",
58-
"role_users",
59-
"user_attribute_user_values",
60-
"search_roles",
61-
"create_group",
62-
"set_role_groups",
63-
"set_user_attribute_group_values",
64-
"set_user_attribute_user_value",
65-
"create_user_credentials_email",
66-
"send_user_credentials_email_password_reset",
67-
"create_user_credentials_api3",
68-
"add_group_user",
69-
"update_user",
70-
"create_user",
71-
"search_groups_with_roles",
72-
"role_groups"
73-
]
74-
oauth2_urls: []
75-
scoped_user_attributes: ["sheet_id", "token_server_url"]
76-
}
77-
}
78-
```
79-
80-
**Note** that http://localhost:8081/\* points to the access token server. Change to the access token server URL you are using.
9+
Until the Hackathon extension is available via the Looker Marketplace, a Hackathon manifest needs to be used. [Example Hackathon manifest](manifest.lkml)
8110

8211
Remember to add a model to the project that has a valid connection.
8312

@@ -115,15 +44,8 @@ Anyone who has the Looker `Admin` role is also a Hackathon administrator because
11544

11645
You need to be a Looker Admin to set up the Hackathon extension and add users.
11746

118-
- Copy the Hackathon GSheets template to a new sheet. (To be linked)
119-
- Add a new row in the `hackathons` tab for your hackathon. To override which hackathon the extension views, set the `default` column of the desired hackathon row to `TRUE`.
120-
- Grant your service account access to the new GSheet
47+
- Add a new row in the `hackathons` tab for your hackathon. To override which hackathon the extension views, set the `default` column of the desired hackathon row to `TRUE`. TODO: Update with artifact api change.
12148
- Open the Hackathon extension in Looker.
122-
- Configure Hackathon extension GSheet connection
123-
- Admin | Configuration
124-
- Provide the requested values
125-
- Submit
126-
- Reload the page
12749
- See the banner listing the desired hackathon in the Welcome message
12850
- Create `Hackathon` role with permissions you deem necessary for hackathon users.
12951
- Add users through app, which will create the hackathon group and assign the `Hackathon` role and accomplish other administrative tasks.

packages/hackathon/artifact.ipynb

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"DATA = {\n",
10+
" 'client_id':'',\n",
11+
" 'client_secret':''\n",
12+
"}"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": null,
18+
"metadata": {},
19+
"outputs": [],
20+
"source": [
21+
"# Login\n",
22+
"import requests \n",
23+
"\n",
24+
"response = requests.post('http://localhost:19999/login',data=DATA)\n",
25+
"\n",
26+
"access_token = response.json()['access_token']\n",
27+
"headers= {\n",
28+
" 'Authorization': 'token ' + access_token,\n",
29+
" 'x-looker-appid': 'marketplace_extension_hackathon::hackathon'\n",
30+
"}\n",
31+
"response = requests.get(\n",
32+
" 'http://localhost:19999/api/4.0/artifact/namespaces',\n",
33+
" headers=headers)\n",
34+
"\n",
35+
"print(response.status_code)"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": null,
41+
"metadata": {},
42+
"outputs": [],
43+
"source": [
44+
"import json\n",
45+
"TABLE_NAME = 'User'\n",
46+
"\n",
47+
"response = requests.get(\n",
48+
" 'http://localhost:19999/api/4.0/artifact/hackathon/search',\n",
49+
" data={'key': TABLE_NAME + '%'},\n",
50+
" headers=headers)\n",
51+
"\n",
52+
"print(response.json())"
53+
]
54+
},
55+
{
56+
"cell_type": "code",
57+
"execution_count": null,
58+
"metadata": {},
59+
"outputs": [],
60+
"source": [
61+
"# Migrate all data \n",
62+
"# Download each google sheet as csv\n",
63+
"# Expects directory/naming to be: csv/hackathons.csv\n",
64+
"import json\n",
65+
"import csv\n",
66+
"\n",
67+
"def toValues(path):\n",
68+
" with open(path, newline='') as csvfile:\n",
69+
" reader = csv.reader(x.replace('\\0', '') for x in csvfile)\n",
70+
" header = next(reader)\n",
71+
" values = []\n",
72+
" for row in reader:\n",
73+
" value = {}\n",
74+
" for index, v in enumerate(row):\n",
75+
" value[header[index]] = v\n",
76+
" values.append(value)\n",
77+
" return values\n",
78+
" \n",
79+
"def toArtifact(key, value):\n",
80+
" artifact = {}\n",
81+
" artifact['key'] = key\n",
82+
" artifact['content_type'] = 'application/json'\n",
83+
" artifact['value'] = json.dumps(value)\n",
84+
" return artifact\n",
85+
" \n",
86+
"artifacts = []\n",
87+
"\n",
88+
"for v in toValues('csv/hackathons.csv'):\n",
89+
" key = 'Hackathon:' + v['_id']\n",
90+
" del v['_id'] \n",
91+
" artifacts.append(toArtifact(key, v))\n",
92+
"\n",
93+
"for v in toValues('csv/judgings.csv'):\n",
94+
" key = 'Judging:' + v['_id']\n",
95+
" del v['_id'] \n",
96+
" v['user_id'] = 'User:' + v['user_id']\n",
97+
" v['project_id'] = 'Project:' + v['project_id']\n",
98+
" artifacts.append(toArtifact(key, v))\n",
99+
"\n",
100+
"for v in toValues('csv/projects.csv'):\n",
101+
" key = 'Project:' + v['_id']\n",
102+
" del v['_id']\n",
103+
" v['_user_id'] = 'User:' + v['_user_id']\n",
104+
" v['_hackathon_id'] = 'Hackathon:' + v['_hackathon_id']\n",
105+
" v['technologies'] = ['Technology:' + x for x in v['technologies'].split(',')]\n",
106+
" artifacts.append(toArtifact(key, v))\n",
107+
"\n",
108+
"for v in toValues('csv/registrations.csv'):\n",
109+
" key = 'Registration:' + v['_id']\n",
110+
" del v['_id'] \n",
111+
" v['_user_id'] = 'User:' + v['_user_id']\n",
112+
" v['hackathon_id'] = 'Hackathon:' + v['hackathon_id']\n",
113+
" artifacts.append(toArtifact(key, v))\n",
114+
"\n",
115+
"for v in toValues('csv/team_members.csv'):\n",
116+
" key = 'TeamMember:' + v['_id']\n",
117+
" del v['_id'] \n",
118+
" v['user_id'] = 'User:' + v['user_id']\n",
119+
" v['project_id'] = 'Project:' + v['project_id']\n",
120+
" artifacts.append(toArtifact(key, v))\n",
121+
"\n",
122+
"for v in toValues('csv/technologies.csv'):\n",
123+
" key = 'Technology:' + v['_id']\n",
124+
" del v['_id'] \n",
125+
" artifacts.append(toArtifact(key, v))\n",
126+
"\n",
127+
"for v in toValues('csv/users.csv'):\n",
128+
" key = 'User:' + v['_id']\n",
129+
" v['looker_id'] = '' + v['_id']\n",
130+
" del v['_id'] \n",
131+
" artifacts.append(toArtifact(key, v))\n",
132+
"\n",
133+
"response = requests.put('http://localhost:19999/api/4.0/artifacts/hackathon', headers=headers, json=artifacts) \n",
134+
"\n",
135+
"print(response.status_code)\n"
136+
]
137+
},
138+
{
139+
"cell_type": "code",
140+
"execution_count": null,
141+
"metadata": {},
142+
"outputs": [],
143+
"source": [
144+
"# TO PURGE ALL TABLES. BE CAREFUL\n",
145+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/Hackathon/purge',headers=headers)\n",
146+
"print(response.status_code)\n",
147+
"\n",
148+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/Judging/purge',headers=headers)\n",
149+
"print(response.status_code)\n",
150+
"\n",
151+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/Project/purge',headers=headers)\n",
152+
"print(response.status_code)\n",
153+
"\n",
154+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/Registration/purge',headers=headers)\n",
155+
"print(response.status_code)\n",
156+
"\n",
157+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/TeamMember/purge',headers=headers)\n",
158+
"print(response.status_code)\n",
159+
"\n",
160+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/Technology/purge',headers=headers)\n",
161+
"print(response.status_code)\n",
162+
"\n",
163+
"response = requests.delete('http://localhost:19999/api/4.0/artifact/User/purge',headers=headers)\n",
164+
"print(response.status_code)\n"
165+
]
166+
},
167+
{
168+
"cell_type": "code",
169+
"execution_count": null,
170+
"metadata": {},
171+
"outputs": [],
172+
"source": []
173+
}
174+
],
175+
"metadata": {
176+
"kernelspec": {
177+
"display_name": "Python 3.10.7 64-bit",
178+
"language": "python",
179+
"name": "python3"
180+
},
181+
"language_info": {
182+
"codemirror_mode": {
183+
"name": "ipython",
184+
"version": 3
185+
},
186+
"file_extension": ".py",
187+
"mimetype": "text/x-python",
188+
"name": "python",
189+
"nbconvert_exporter": "python",
190+
"pygments_lexer": "ipython3",
191+
"version": "3.10.8"
192+
},
193+
"orig_nbformat": 4,
194+
"vscode": {
195+
"interpreter": {
196+
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
197+
}
198+
}
199+
},
200+
"nbformat": 4,
201+
"nbformat_minor": 2
202+
}

packages/hackathon/manifest.lkml

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
application: hackathon {
2+
label: "Hackathon"
3+
url: "https://localhost:8080/dist/bundle.js"
4+
# file: "bundle.js"
5+
entitlements: {
6+
local_storage: no
7+
navigation: yes
8+
new_window: yes
9+
new_window_external_urls: [
10+
"https://*.looker.com/*",
11+
"https://*.google.com/*",
12+
"https://*.bit.ly/*",
13+
"https://*.imgur.com/*",
14+
"https://*.slack.com/*",
15+
"https://*.github.com/*",
16+
"https://*.youtube.com/*",
17+
"https://*.youtu.be/*",
18+
"https://*.vimeo.com/*"
19+
]
20+
use_form_submit: yes
21+
use_embeds: no
22+
use_iframes: no
23+
use_clipboard: no
24+
core_api_methods: [
25+
"me",
26+
"all_roles",
27+
"all_user_attributes",
28+
"delete_user_attribute",
29+
"create_user_attribute",
30+
"search_groups",
31+
"search_users",
32+
"user_roles",
33+
"role_users",
34+
"user_attribute_user_values",
35+
"search_roles",
36+
"create_group",
37+
"set_role_groups",
38+
"set_user_attribute_group_values",
39+
"set_user_attribute_user_value",
40+
"create_user_credentials_email",
41+
"send_user_credentials_email_password_reset",
42+
"create_user_credentials_api3",
43+
"add_group_user",
44+
"update_user",
45+
"create_user",
46+
"search_groups_with_roles",
47+
"role_groups",
48+
"search_artifacts",
49+
"artifact_namespaces",
50+
"artifact_value",
51+
"purge_artifacts",
52+
"artifact",
53+
"delete_artifact",
54+
"update_artifacts"
55+
]
56+
}
57+
}

packages/hackathon/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@looker/icons": "^1.5.3",
4343
"@looker/sdk": "^22.20.0",
4444
"@looker/sdk-rtl": "^21.4.0",
45+
"@looker/wholly-artifact": "^0.1.0",
4546
"@looker/wholly-sheet": "^0.5.37",
4647
"@styled-icons/material": "^10.28.0",
4748
"@styled-icons/material-outlined": "^10.28.0",

0 commit comments

Comments
 (0)