Skip to content

Commit ece01d6

Browse files
committed
Fix bugs from workshop, additional clarification
1 parent 112f1a3 commit ece01d6

File tree

15 files changed

+155
-201
lines changed

15 files changed

+155
-201
lines changed

docs/access-control/index.md

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,22 @@ First, let's create our hardcoded user mappings in `cube.py`. In practice, thes
7171
Make sure to update your user email in the first entry of the `USER_SECURITY_MAPPINGS` dictionary below with your workshop email address. This will ensure you have full access to the data when logged in to D3 in the final workshop module.
7272
:::
7373

74-
```python title="cube.py" {3-43}
74+
```python title="cube.py" {3-45}
7575
from cube import config
7676

7777
USER_SECURITY_MAPPINGS = {
7878
# YOUR USER - UPDATE WITH YOUR WORKSHOP EMAIL
7979
"wdc-2025-999@example.com": {
8080
"role": "global_admin",
81-
"filter_type": "none"
81+
"filter_type": "none",
82+
"show_pii": "true" # Admins can see PII data
8283
},
8384

8485
# Global admin - sees everything
8586
"admin@tpch.com": {
8687
"role": "global_admin",
87-
"filter_type": "none"
88+
"filter_type": "none",
89+
"show_pii": "true" # Admins can see PII data
8890
},
8991

9092
# Regional director - North America (Region 1)
@@ -134,7 +136,7 @@ def query_rewrite(query: dict, ctx: dict) -> dict:
134136
if not user_security:
135137
# Add impossible filter to return no data
136138
query['filters'].append({
137-
'member': 'sales.c_custkey',
139+
'member': 'customers.customer_key',
138140
'operator': 'equals',
139141
'values': ['-1'] # Non-existent customer
140142
})
@@ -236,6 +238,10 @@ In this example, we'll mask phone numbers for sales reps and regional directors,
236238
4. Extend the security context to lookup the user's PII access
237239
5. Add the `context_to_app_id` function to cache our model both with PII access and without PII access
238240

241+
:::note
242+
If you save the model changes in between steps, you may see compile errors. Just make all 5 changes and then save, or ignore the compile errors until you finish all 5 steps.
243+
:::
244+
239245
### Step 1: Update the Phone Dimension
240246

241247
Update the existing phone dimension in your `customers` cube to include PII masking:
@@ -260,7 +266,7 @@ Update the existing phone dimension in your `customers` cube to include PII mask
260266

261267
We also need to include the phone field in our sales view. Update your `sales` view definition to move the phone dimension from `excludes` to `includes`:
262268

263-
```yaml title="/model/views/sales.yml" {11}
269+
```yaml title="/model/views/sales.yml" {11,13}
264270
...
265271

266272
# Customer information via orders
@@ -273,6 +279,7 @@ We also need to include the phone field in our sales view. Update your `sales` v
273279
- customer_key
274280
- phone
275281
excludes:
282+
- phone # REMOVE THIS LINE
276283
- address
277284
- comment
278285

@@ -321,17 +328,17 @@ def extend_context(req: dict) -> dict:
321328
# Check if securityContext exists, skip extending context if not
322329
if 'securityContext' not in req:
323330
return req
324-
325-
# Get user from security context - handle both React app and D3 formats
331+
332+
# Get user from security context
326333
security_context = req.get('securityContext', {})
327-
# If D3 format (has cubeCloud key), extract username from cubeCloud.username
328-
if 'cubeCloud' in security_context:
329-
user_id = security_context.get('cubeCloud', {}).get('username')
330-
# Update the user_id in the security context
331-
req['securityContext']['user_id'] = user_id
332-
else:
333-
# For React app format, use existing user_id
334-
user_id = security_context.get('user_id', 'anonymous')
334+
335+
# If user_id is not in the securityContext, extract it from cubeCloud.username
336+
if 'user_id' not in security_context:
337+
if 'cubeCloud' in security_context:
338+
# Update the user_id in the security context
339+
req['securityContext']['user_id'] = security_context.get('cubeCloud', {}).get('username')
340+
341+
user_id = security_context.get('user_id', 'anonymous')
335342

336343
# Look up user security settings
337344
user_security = USER_SECURITY_MAPPINGS.get(user_id, {})
@@ -381,26 +388,8 @@ In this section, we'll create two new views:
381388

382389
First, we need to ensure user roles are available in the security context. Update your `extend_context` function in `cube.py` to include the user's role. We'll also make our function more robust to handle both the React app and D3 formats of the security context:
383390

384-
```python title="/cube.py" {9-19,26}
391+
```python title="/cube.py" {8}
385392
...
386-
387-
@config('extend_context')
388-
def extend_context(req: dict) -> dict:
389-
# Check if securityContext exists, skip extending context if not
390-
if 'securityContext' not in req:
391-
return req
392-
393-
# Get user from security context - handle both React app and D3 formats
394-
security_context = req.get('securityContext', {})
395-
396-
# If D3 format (has cubeCloud key), extract username from cubeCloud.username
397-
if 'cubeCloud' in security_context:
398-
user_id = security_context.get('cubeCloud', {}).get('username')
399-
# Update the user_id in the security context
400-
req['securityContext']['user_id'] = user_id
401-
else:
402-
# For React app format, use existing user_id
403-
user_id = security_context.get('user_id', 'anonymous')
404393

405394
# Look up user security settings
406395
user_security = USER_SECURITY_MAPPINGS.get(user_id, {})
@@ -531,6 +520,9 @@ views:
531520
1. **Navigate to Playground**
532521
2. **Test each user type and observe available views**:
533522

523+
![Hidden view verification for Director NA](./hidden_view.png)
524+
The padlock icon means this view (or cube or field) is not visible to the current user based on their security context through the APIs.
525+
534526
**Admin User**:
535527
```json
536528
{ "user_id": "admin@tpch.com" }
@@ -556,8 +548,7 @@ views:
556548
- Sarah sees only her customers with masked phone numbers
557549
- Admin sees everything including real phone numbers
558550

559-
![Hidden view verification for Director NA](./hidden_view.png)
560-
The padlock icon means this view (or cube or field) is not visible to the current user based on their security context through the APIs.
551+
561552

562553
### Advanced: Combining View Visibility with Row-Level Security
563554

@@ -646,7 +637,7 @@ Now that you've implemented comprehensive access control, let's save your work:
646637

647638
**Step 2: Commit Your Changes**
648639
1. **Navigate to the Data Model page**
649-
2. **Review all your changes** - You should see modified files: `cube.py`, `globals.py`, `sales.yml`, `director_dashboard.yml`, `sales_team.yml`
640+
2. **Review all your changes** - You should see modified files: `customers.yml`, `cube.py`, `globals.py`, `sales.yml`, `director_dashboard.yml`, `sales_team.yml`
650641
3. **Click "Commit & Sync"**
651642
4. **Add a descriptive commit message**:
652643
```

docs/apis/index.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,25 @@ To make REST API calls, you'll need your Bearer token from Cube Cloud. This JWT
6969
Keep your Bearer token secure! It grants access to your data based on the security context you provide.
7070
:::
7171

72+
### Getting Your API Endpoint
73+
To make REST API calls, you'll need your Cube endpoint URL. This is available in the **Integrations** page under the **API Credentials** section.
74+
75+
![Endpoint](./endpoint.png)
76+
7277
:::tip Production vs Development API Endpoints
7378
Now that you've deployed your complete data model to production, you have access to different API endpoints:
7479

7580
**Production Endpoint** (recommended for this section):
7681
```
77-
https://your-workspace.cubecloud.dev/cubejs-api/v1
82+
https://your-workspace.your-cloud-region.cubecloudapp.dev/cubejs-api/v1
7883
```
7984
- Stable, deployed version of your data model
8085
- Full performance with production-scale resources (NOTE: just for this workshop, our prod environment is still scaled down to use dev-scale resources)
8186
- What real applications should use
8287

8388
**Development Endpoint** (only when in Dev Mode):
8489
```
85-
https://your-workspace.cubecloud.dev/dev-branch-name/cubejs-api/v1
90+
https://your-workspace.your-cloud-region.cubecloudapp.dev/dev-branch-or-user-name/cubejs-api/v1
8691
```
8792
- Development-scale resources (smaller, slower)
8893
- For testing changes before deployment
@@ -93,9 +98,11 @@ https://your-workspace.cubecloud.dev/dev-branch-name/cubejs-api/v1
9398

9499
### Exercise: Your First API Call
95100

96-
Now that you have your Bearer token, let's query TPCH's monthly revenue using the REST API. We'll use the GET method with URL-encoded query parameters - this is simpler for testing in the terminal.
101+
Let's query TPCH's monthly revenue using the REST API. We'll use the GET method with URL-encoded query parameters - this is simpler for testing in the terminal.
97102

98-
Head to the **Integrations** page and click the **API Credentials** button, then the **REST API** tab. This will show you your endpoint URL and how to construct a query using curl.
103+
:::tip
104+
On the **Integrations** page and click the **API Credentials** button, then the **REST API** tab. This will show you your endpoint URL and how to construct a query using `curl`.
105+
:::
99106

100107
import Tabs from '@theme/Tabs';
101108
import TabItem from '@theme/TabItem';

docs/caching/index.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,6 @@ Add this to your `orders.yml` cube:
330330

331331
**Save** your changes to the cube.
332332

333-
**Dashboard possibilities:**
334-
- 📈 **Customer Segment Performance** - Compare segments over time
335-
- 📦 **Order Size Distribution** - Understand purchase patterns
336-
- ✅ **Order Status Monitoring** - Track fulfillment metrics
337-
- 👥 **Customer Value Analysis** - Identify high-value customers
338-
339333
**Create the Customer Behavior View:**
340334

341335
Before testing, create a view optimized for customer behavior analysis. Create a new file `customer_behavior.yml` in your `model/views` folder:
@@ -661,7 +655,7 @@ Now that you've implemented sophisticated caching with pre-aggregations, let's s
661655

662656
**Step 2: Commit Your Changes**
663657
1. **Navigate to the Data Model page**
664-
2. **Review all your changes** - You should see all modified files from the entire workshop: data modeling, access control, and caching changes
658+
2. **Review all your changes** - You should see our modified files : `line_items.yml`, `orders.yml`, and `customer_behavior.yml`
665659
3. **Click "Commit & Sync"**
666660
4. **Add a descriptive commit message**:
667661
```

docs/d3-analytics/index.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,15 @@ Cube D3 is currently in private preview and may have some limitations or errors
4242
:::
4343

4444
In Cube Cloud:
45-
1. **Navigate to D3** (click the Cube logo in the top left, then select "D3 AI")
46-
2. **Click Admin** in the left sidebar
45+
1. **Navigate to D3** (click the Cube logo in the top left, then select "D3 AI") The interface will look like it's still loading, but move on to the next step anyway.
46+
2. **Click Admin** at the bottom of the left sidebar
4747
3. In **Spaces**, click **Add Space**
4848
4. **Name your space** (e.g. "TPCH Analytics")
4949
5. In **Agents**, click **Add Agent**
5050
6. **Name your agent** (e.g. "TPCH Analyst")
51-
7. Select **"WDC 2025 Workshop"** as the **Semantic Model**
51+
7. Select **"WDC 2025"** as the **Semantic Model**
5252
8. Assign it to the **TPCH Analytics** space you just created
5353
9. Navigate **"Back to app"** to return to the D3 interface
54-
10. Make sure your Space is selected in the top dropdown menu.
5554

5655
## Exercise: Interacting with D3
5756

@@ -62,6 +61,18 @@ Ask D3 a simple question about your data:
6261
What is our total revenue this year?
6362
```
6463

64+
:::tip
65+
Don't see any data? Make sure you updated your cube.py file with your username (email address) in the `USER_SECURITY_MAPPINGS` section. This allows D3 to recognize you as an admin and access the data.
66+
```USER_SECURITY_MAPPINGS = {
67+
# YOUR USER - UPDATE WITH YOUR WORKSHOP EMAIL
68+
"wdc-2025-044@example.com": {
69+
"role": "global_admin",
70+
"filter_type": "none",
71+
"show_pii": "true" # Admins can see PII data
72+
},
73+
```
74+
:::
75+
6576
Notice on the right side, D3 shows you the **Semantic Views** you have access to. If you click on them, you can see the same fields we added to the views in our prior modules.
6677

6778
As the agent processes your request, it will generate a response and display the results in a table or chart format. You can also see the underlying **Semantic SQL** query that was generated. Click on the **Semantic SQL** tab to view it.
323 KB
Loading

docs/data-modeling/index.md

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ You should see a time series of order counts. Check out the tabs for **SQL API*
5353

5454
Now click on the **Generated SQL** tab - this is the query that Cube generates which goes back to our PostgreSQL database.
5555

56-
Let's **add another field to the query**. From the `regions` cube, click the `name` field. Now check those API tabs again. The "Semantic SQL" (what we see on the **SQL API** tab) is simple - you're just asking for fields without worrying about the join logic which we've already defined and is now part of the data model.
56+
Let's **add another field to the query**. From the `customer_regions` cube, click the `name` field. Now check those API tabs again. The "Semantic SQL" (what we see on the **SQL API** tab) is simple - you're just asking for fields without worrying about the join logic which we've already defined and is now part of the data model.
5757

5858
On the **Generated SQL** tab, the query is now much more complex as the joins traverse a few tables to go from `orders` -> `customers` -> `nations` -> `regions` but as a semantic layer end user, we are able to simply re-use the logic that was already setup ahead of time.
5959

@@ -171,8 +171,11 @@ In development mode, you'll see a separate API endpoint for testing:
171171
# Production API (for live apps)
172172
https://your-workspace.cubecloud.dev/cubejs-api/v1
173173
174-
# Development API (for testing changes)
175-
https://your-workspace.cubecloud.dev/dev-branch-name/cubejs-api/v1
174+
# Non-Prod Branch API (for testing changes)
175+
https://your-workspace.cubecloud.dev/branch-name/cubejs-api/v1
176+
177+
# Dev Mode Branch API (for testing changes)
178+
https://your-workspace.cubecloud.dev/branch-name/user-name/cubejs-api/v1
176179
```
177180

178181
### Making Changes Safely
@@ -513,7 +516,9 @@ Create business-friendly categories by adding binning dimensions. Instead of de
513516
Create a business-critical metric using subqueries and measure dependencies. Let's build **Average Customer Value (ACV)** in the customers cube:
514517

515518
**Step 1: Add a subquery dimension to customers.yml**
516-
```yaml title="model/cubes/customers.yml" {7-12}
519+
```yaml title="model/cubes/customers.yml" {9-14}
520+
...
521+
dimensions:
517522
...
518523
519524
- name: comment
@@ -696,9 +701,11 @@ You simply define a view that includes all the measures and related dimensions y
696701

697702
## Creating a Sales View
698703

699-
To create a view, it's a best practice define it in a separate YAML file. **Important**: Generally you'll use the lowest granularity cube with the measures you want in the view - typically one tied to a fact table as your base. In our case, that's `line_items` since it contains the actual sales transactions:
704+
To create a view, it's a best practice to define it in a separate YAML file in the `views` directory to help keep them organized. Since we don't have that yet, let's create it now. Hover over the `/model` directory in the Cube Cloud IDE and click the **...** menu, then the **Add Directory** button. Name the folder `views`.
700705

701-
Hover over the `/model/views` directory in the Cube Cloud IDE and click the **...*** menu, then the **New File** button. Name the file `sales.yml` and add the following content:
706+
![Add views folder](./add-views-folder.png)
707+
708+
Now let's add a new file for our new view. Hover over the `/model/views` directory in the Cube Cloud IDE and click the **...** menu, then the **New File** button. Name the file `sales.yml` and add the following content:
702709

703710
```yaml title="model/views/sales.yml"
704711
views:
@@ -781,6 +788,8 @@ views:
781788
- region_key
782789
```
783790

791+
**Important**: Generally you'll use the lowest granularity cube with the measures you want in the view - typically one tied to a fact table as your base. In our case, that's `line_items` since it contains the actual sales transactions.
792+
784793
## What This Enables
785794

786795
With this view, end users can:
@@ -831,26 +840,7 @@ With this view, end users can:
831840
3. **Clean Schema**: Only relevant, business-friendly fields
832841
4. **Accurate Metrics**: Revenue and quantity calculations are precise
833842

834-
## Complete Model Files
835-
836-
### 📁 Starter Files
837-
**Location**: `static/cube-models/starter/`
838-
839-
Contains:
840-
- Basic cubes with dimensions and count measures
841-
- Most joins (missing one for exercise)
842-
- Foundation for building upon
843-
844-
### 📁 Data Modeling Complete
845-
**Location**: `static/cube-models/data-modeling/`
846-
847-
Contains:
848-
- All revenue measures
849-
- Subquery dimensions
850-
- Binning dimensions
851-
- Multi-level measure dependencies
852-
- Complete join relationships
853-
- **Sales View** - Denormalized cube perfect for dashboards and reporting
843+
This data model is well on its way to power dashboards, reports, and applications with consistent, reliable business metrics.
854844

855845
## Deploying Your Sales View to Production
856846

@@ -859,15 +849,20 @@ Now that you've built a comprehensive sales view with all the business metrics,
859849
### Final Exercise: Commit your Changes
860850

861851
**Step 1: Review Your Changes**
862-
1. **Ensure you're in Development Mode** - Look for the "Dev Mode" indicator
852+
1. **Ensure you're still in Development Mode** - Look for the "Dev Mode" indicator
863853
2. **Test your sales view** one final time in the Playground:
864854
- Query `sales.total_sales_amount` by `sales.region`
865855
- Query `sales.parts_brand` with `sales.total_quantity`
866856
- Verify all field names and calculations are correct
867857

858+
![Views Playground Button](./views-playground-button.png)
859+
868860
**Step 2: Commit Your Development Branch**
869861
1. **Navigate to the Data Model page**
870862
2. **Review all your changes** - You should see modified files and changes highlighted on the **Changes** diff tab
863+
864+
![Review Changes](./review-changes.png)
865+
871866
3. **Click Commit & Sync**
872867
4. **Add a descriptive commit message**:
873868
```
@@ -889,19 +884,17 @@ Congratulations! You've successfully:
889884
✅ **Learned Cube's development workflow** - Safe, isolated development with Git integration
890885
✅ **Enhanced the orders cube** - Added revenue metrics and business logic
891886
✅ **Built complex relationships** - Subqueries, joins, and measure dependencies
892-
✅ **Created a comprehensive sales view** - Line item grain with rich dimensional context
893-
✅ **Deployed to production** - Your changes are now live and ready for applications
887+
✅ **Created a comprehensive sales view** - Line item grain with rich dimensional context
894888

895-
### Business-Friendly Data Model
889+
## Complete Model Files
890+
If you need to reference our starting or ending model state, here are the links to the files:
896891

897-
Your sales view now provides:
898-
- **🎯 Correct granularity**: Line item level for accurate transaction analysis
899-
- **📊 Key business metrics**: Revenue, quantity, discounts with proper calculations
900-
- **🌐 Rich context**: Product, customer, supplier, and geographic dimensions
901-
- **🔗 Clean relationships**: Proper join paths preventing fan-out issues
902-
- **📝 Business-friendly naming**: Intuitive field names and aliases
892+
### 📁 Starter Files
893+
**Location**: [1-starter](https://github.com/cube-js/cube-workshop/tree/main/static/cube-models/1-starter)
894+
895+
### 📁 Data Modeling Complete
896+
**Location**: [2-data-modeling](https://github.com/cube-js/cube-workshop/tree/main/static/cube-models/2-data-modeling)
903897

904-
This data model is well on its way to power dashboards, reports, and applications with consistent, reliable business metrics.
905898

906899
---
907900

994 KB
Loading
258 KB
Loading

0 commit comments

Comments
 (0)