Repository navigation
fix(m365): exclude unlicensed users from collaboration silver feeders #1869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1243,6 +1243,7 @@ CREATE TABLE IF NOT EXISTS bronze_m365.teams_activity ( | |
| reportRefreshDate Nullable(String), | ||
| reportPeriod Nullable(String), | ||
| lastActivityDate Nullable(String), | ||
| isLicensed Nullable(Bool), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Reconcile existing M365 bronze tables, not only fresh placeholders. These columns are added only when the table is created. On a warm ClickHouse/E2E database, Also applies to: 1310-1310, 1334-1334 🤖 Prompt for AI Agents |
||
| teamChatMessageCount Nullable(Decimal(38, 9)), | ||
| privateChatMessageCount Nullable(Decimal(38, 9)), | ||
| postMessages Nullable(Decimal(38, 9)), | ||
|
|
@@ -1306,6 +1307,7 @@ CREATE TABLE IF NOT EXISTS bronze_m365.onedrive_activity ( | |
| reportRefreshDate Nullable(String), | ||
| reportPeriod Nullable(String), | ||
| lastActivityDate Nullable(String), | ||
| assignedProducts Nullable(String), | ||
| viewedOrEditedFileCount Nullable(Decimal(38, 9)), | ||
| syncedFileCount Nullable(Decimal(38, 9)), | ||
| sharedInternallyFileCount Nullable(Decimal(38, 9)), | ||
|
|
@@ -1329,6 +1331,7 @@ CREATE TABLE IF NOT EXISTS bronze_m365.sharepoint_activity ( | |
| reportRefreshDate Nullable(String), | ||
| reportPeriod Nullable(String), | ||
| lastActivityDate Nullable(String), | ||
| assignedProducts Nullable(String), | ||
| viewedOrEditedFileCount Nullable(Decimal(38, 9)), | ||
| syncedFileCount Nullable(Decimal(38, 9)), | ||
| sharedInternallyFileCount Nullable(Decimal(38, 9)), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| spec_version: 1 | ||
| description: > | ||
| Regression: unlicensed M365 users must NOT leak into collaboration metrics (#736). #magic___^_^___line The M365 teams_activity report includes unlicensed users (guests, ex-employees, service accounts) flagged `isLicensed=false`. The silver feeder m365__collab_meeting_activity now filters them out with `coalesce(isLicensed, true) = true`, so their activity never reaches gold. #magic___^_^___line Setup — two Engineering people, both with a Teams meeting row dated 2026-12-25: | ||
| • alice — isLicensed=true, meetingsAttendedCount=3 → licensed CONTROL, value 3 | ||
| • frank — isLicensed=false, meetingsAttendedCount=99 → unlicensed, EXCLUDED (null) | ||
|
|
||
| If the license filter regressed, frank's period value would be 99 instead of null and this test would fail. alice proves the pipeline itself is healthy, so a failure isolates to the license filter rather than a broken build. | ||
|
|
||
| bronze: | ||
| bronze_bamboohr.employees: | ||
| - $ref: templates/people.yaml#/templates/alice | ||
| - {$ref: templates/people.yaml#/templates/bamboohr_employee, id: e099, unique_key: bamboohr-test-e099, firstName: Frank, lastName: Guest, displayName: Frank Guest, workEmail: frank@example.com,} | ||
|
|
||
| bronze_m365.teams_activity: | ||
| - {$ref: templates/m365_teams.yaml#/templates/m365_teams, userPrincipalName: alice@example.com, reportRefreshDate: "2026-12-25", unique_key: tm-alice-20261225, isLicensed: true, meetingsAttendedCount: 3} | ||
| - {$ref: templates/m365_teams.yaml#/templates/m365_teams, userPrincipalName: frank@example.com, reportRefreshDate: "2026-12-25", unique_key: tm-frank-20261225, isLicensed: false, meetingsAttendedCount: 99,} | ||
|
|
||
| cases: | ||
| - name: Unlicensed user excluded, licensed control present | ||
| request: | ||
| url: /v1/metric-results | ||
| method: POST | ||
| body: | ||
| entity: {type: person, ids: [alice@example.com, frank@example.com]} | ||
| period: {from: "2026-12-01", to: "2026-12-31"} | ||
| metrics: | ||
| - metric_key: collab.meetings_count | ||
| views: | ||
| - {view: period} | ||
| expect: | ||
| - assert: "status == 200" | ||
| # Licensed control: activity flows through as usual. | ||
| - metric: collab.meetings_count | ||
| view: period | ||
| find: {entity_id: alice@example.com} | ||
| equal: {value: 3} | ||
| # Unlicensed user: filtered out at silver → no gold observation → null. | ||
| - metric: collab.meetings_count | ||
| view: period | ||
| find: {entity_id: frank@example.com} | ||
| equal: {value: null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: constructorfabric/insight
Length of output: 20094
🏁 Script executed:
Repository: constructorfabric/insight
Length of output: 20077
Add a one-time cleanup for newly excluded unlicensed activity.
These models are
materialized='incremental', so the new license predicates filter future source selections but do not remove rows already materialized forisLicensed = falseor explicitly emptyassignedProducts. Add a cleanup/full refresh or explicit incremental deletion for all five models.📍 Affects 5 files
src/ingestion/connectors/collaboration/m365/dbt/m365__collab_meeting_activity.sql#L45-L51(this comment)src/ingestion/connectors/collaboration/m365/dbt/m365__collab_chat_activity.sql#L70-L76src/ingestion/connectors/collaboration/m365/dbt/m365__collab_email_activity.sql#L34-L42src/ingestion/connectors/collaboration/m365/dbt/m365__collab_document_activity_onedrive.sql#L39-L47src/ingestion/connectors/collaboration/m365/dbt/m365__collab_document_activity_sharepoint.sql#L39-L47🤖 Prompt for AI Agents