@squadbase/vite-server 0.1.17-dev.24af54e → 0.1.17-dev.423ee34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +4873 -1073
- package/dist/connectors/airtable-oauth.js +78 -11
- package/dist/connectors/airtable.js +74 -11
- package/dist/connectors/amplitude.js +38 -11
- package/dist/connectors/anthropic.js +4 -2
- package/dist/connectors/asana.js +67 -13
- package/dist/connectors/attio.js +60 -16
- package/dist/connectors/aws-billing.js +38 -11
- package/dist/connectors/azure-sql.js +64 -13
- package/dist/connectors/backlog-api-key.js +70 -18
- package/dist/connectors/clickup.js +80 -13
- package/dist/connectors/cosmosdb.js +42 -15
- package/dist/connectors/customerio.js +39 -12
- package/dist/connectors/dbt.js +716 -28
- package/dist/connectors/freshdesk.js +112 -11
- package/dist/connectors/freshsales.js +38 -11
- package/dist/connectors/freshservice.js +38 -11
- package/dist/connectors/gamma.js +47 -20
- package/dist/connectors/gemini.js +4 -2
- package/dist/connectors/github.js +42 -15
- package/dist/connectors/gmail-oauth.js +38 -13
- package/dist/connectors/gmail.js +34 -7
- package/dist/connectors/google-ads.js +38 -11
- package/dist/connectors/google-analytics-oauth.js +182 -28
- package/dist/connectors/google-analytics.js +653 -104
- package/dist/connectors/google-audit-log.js +34 -7
- package/dist/connectors/google-calendar-oauth.js +91 -18
- package/dist/connectors/google-calendar.js +91 -14
- package/dist/connectors/google-docs.js +38 -13
- package/dist/connectors/google-drive.js +60 -13
- package/dist/connectors/google-search-console-oauth.js +156 -20
- package/dist/connectors/google-sheets.js +36 -9
- package/dist/connectors/google-slides.js +38 -13
- package/dist/connectors/grafana.js +75 -13
- package/dist/connectors/hubspot-oauth.js +69 -12
- package/dist/connectors/hubspot.js +55 -12
- package/dist/connectors/influxdb.js +38 -11
- package/dist/connectors/intercom-oauth.js +100 -15
- package/dist/connectors/intercom.js +42 -15
- package/dist/connectors/jdbc.js +36 -9
- package/dist/connectors/jira-api-key.js +98 -14
- package/dist/connectors/kintone-api-token.js +96 -21
- package/dist/connectors/kintone.js +84 -14
- package/dist/connectors/linear.js +84 -15
- package/dist/connectors/linkedin-ads.js +71 -17
- package/dist/connectors/mailchimp-oauth.js +36 -9
- package/dist/connectors/mailchimp.js +36 -9
- package/dist/connectors/meta-ads-oauth.js +63 -17
- package/dist/connectors/meta-ads.js +65 -17
- package/dist/connectors/mixpanel.js +38 -11
- package/dist/connectors/monday.js +39 -12
- package/dist/connectors/mongodb.js +38 -11
- package/dist/connectors/notion-oauth.js +88 -14
- package/dist/connectors/notion.js +90 -14
- package/dist/connectors/openai.js +4 -2
- package/dist/connectors/oracle.js +78 -20
- package/dist/connectors/outlook-oauth.js +48 -23
- package/dist/connectors/powerbi-oauth.js +321 -49
- package/dist/connectors/salesforce.js +72 -12
- package/dist/connectors/semrush.js +374 -52
- package/dist/connectors/sentry.js +66 -13
- package/dist/connectors/shopify-oauth.js +71 -13
- package/dist/connectors/shopify.js +38 -11
- package/dist/connectors/sqlserver.js +64 -13
- package/dist/connectors/stripe-api-key.js +96 -18
- package/dist/connectors/stripe-oauth.js +98 -22
- package/dist/connectors/supabase.js +55 -11
- package/dist/connectors/tableau.js +262 -92
- package/dist/connectors/tiktok-ads.js +67 -19
- package/dist/connectors/wix-store.js +38 -11
- package/dist/connectors/zendesk-oauth.js +83 -15
- package/dist/connectors/zendesk.js +42 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4902 -1077
- package/dist/main.js +4891 -1071
- package/dist/vite-plugin.js +4871 -1071
- package/package.json +1 -1
package/dist/connectors/dbt.js
CHANGED
|
@@ -16,6 +16,7 @@ var init_parameter_definition = __esm({
|
|
|
16
16
|
type;
|
|
17
17
|
secret;
|
|
18
18
|
required;
|
|
19
|
+
isDeprecated;
|
|
19
20
|
constructor(config) {
|
|
20
21
|
this.slug = config.slug;
|
|
21
22
|
this.name = config.name;
|
|
@@ -24,6 +25,7 @@ var init_parameter_definition = __esm({
|
|
|
24
25
|
this.type = config.type;
|
|
25
26
|
this.secret = config.secret;
|
|
26
27
|
this.required = config.required;
|
|
28
|
+
this.isDeprecated = config.isDeprecated ?? false;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Get the parameter value from a ConnectorConnectionObject.
|
|
@@ -435,7 +437,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
435
437
|
/**
|
|
436
438
|
* Create tools for connections that belong to this connector.
|
|
437
439
|
* Filters connections by connectorKey internally.
|
|
438
|
-
* Returns tools keyed as
|
|
440
|
+
* Returns tools keyed as `connector_${connectorKey}_${toolName}`.
|
|
439
441
|
*/
|
|
440
442
|
createTools(connections, config, opts) {
|
|
441
443
|
const myConnections = connections.filter(
|
|
@@ -445,7 +447,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
445
447
|
for (const t of Object.values(this.tools)) {
|
|
446
448
|
const tool = t.createTool(myConnections, config);
|
|
447
449
|
const originalToModelOutput = tool.toModelOutput;
|
|
448
|
-
result[
|
|
450
|
+
result[`connector_${this.connectorKey}_${t.name}`] = {
|
|
449
451
|
...tool,
|
|
450
452
|
toModelOutput: async (options) => {
|
|
451
453
|
if (!originalToModelOutput) {
|
|
@@ -501,19 +503,34 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
501
503
|
};
|
|
502
504
|
let state = flow.initialState();
|
|
503
505
|
let answerIdx = 0;
|
|
506
|
+
const pendingParameterUpdates = [];
|
|
504
507
|
for (const step of flow.steps) {
|
|
505
508
|
const ans = ctx.answers[answerIdx];
|
|
506
509
|
if (ans && ans.questionSlug === step.slug) {
|
|
507
510
|
state = step.applyAnswer(state, ans.answer);
|
|
511
|
+
if (step.toParameterUpdates) {
|
|
512
|
+
pendingParameterUpdates.push(...step.toParameterUpdates(state));
|
|
513
|
+
}
|
|
508
514
|
answerIdx += 1;
|
|
509
515
|
continue;
|
|
510
516
|
}
|
|
517
|
+
const resolvedAllowFreeText = step.allowFreeText !== void 0 ? step.allowFreeText : true;
|
|
511
518
|
if (step.type === "text") {
|
|
519
|
+
if (step.fetchOptions) {
|
|
520
|
+
const options2 = await step.fetchOptions(state, runtime);
|
|
521
|
+
if (options2.length === 0) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
512
525
|
return {
|
|
513
526
|
type: "nextQuestion",
|
|
514
527
|
questionSlug: step.slug,
|
|
515
528
|
question: step.question[ctx.language],
|
|
516
|
-
questionType: "text"
|
|
529
|
+
questionType: "text",
|
|
530
|
+
allowFreeText: resolvedAllowFreeText,
|
|
531
|
+
...pendingParameterUpdates.length > 0 && {
|
|
532
|
+
parameterUpdates: pendingParameterUpdates
|
|
533
|
+
}
|
|
517
534
|
};
|
|
518
535
|
}
|
|
519
536
|
const options = step.fetchOptions ? await step.fetchOptions(state, runtime) : [];
|
|
@@ -525,11 +542,21 @@ async function runSetupFlow(flow, params, ctx, config) {
|
|
|
525
542
|
questionSlug: step.slug,
|
|
526
543
|
question: step.question[ctx.language],
|
|
527
544
|
questionType: step.type,
|
|
528
|
-
options
|
|
545
|
+
options,
|
|
546
|
+
allowFreeText: resolvedAllowFreeText,
|
|
547
|
+
...pendingParameterUpdates.length > 0 && {
|
|
548
|
+
parameterUpdates: pendingParameterUpdates
|
|
549
|
+
}
|
|
529
550
|
};
|
|
530
551
|
}
|
|
531
552
|
const dataInvestigationResult = await flow.finalize(state, runtime);
|
|
532
|
-
return {
|
|
553
|
+
return {
|
|
554
|
+
type: "fulfilled",
|
|
555
|
+
dataInvestigationResult,
|
|
556
|
+
...pendingParameterUpdates.length > 0 && {
|
|
557
|
+
parameterUpdates: pendingParameterUpdates
|
|
558
|
+
}
|
|
559
|
+
};
|
|
533
560
|
}
|
|
534
561
|
async function resolveSetupSelection(params) {
|
|
535
562
|
const { selected, allSentinel, fetchAll, limit } = params;
|
|
@@ -550,11 +577,11 @@ var AUTH_TYPES = {
|
|
|
550
577
|
// ../connectors/src/connectors/dbt/setup.ts
|
|
551
578
|
var dbtOnboarding = new ConnectorOnboarding({
|
|
552
579
|
dataOverviewInstructions: {
|
|
553
|
-
en: `1. Call
|
|
554
|
-
2. For key models, call
|
|
580
|
+
en: `1. Call connector_dbt_request to list models using the GraphQL query for models (first: 100)
|
|
581
|
+
2. For key models, call connector_dbt_request with the model column information query to get column names and types
|
|
555
582
|
3. Explore sources and lineage as needed to understand the data structure`,
|
|
556
|
-
ja: `1.
|
|
557
|
-
2. \u4E3B\u8981\u30E2\u30C7\u30EB\u306B\u3064\u3044\u3066
|
|
583
|
+
ja: `1. connector_dbt_request \u3067\u30E2\u30C7\u30EB\u4E00\u89A7\u53D6\u5F97\u7528\u306EGraphQL\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\uFF08first: 100\uFF09
|
|
584
|
+
2. \u4E3B\u8981\u30E2\u30C7\u30EB\u306B\u3064\u3044\u3066 connector_dbt_request \u3067\u30AB\u30E9\u30E0\u60C5\u5831\u53D6\u5F97\u7528\u306E\u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u3057\u3001\u30AB\u30E9\u30E0\u540D\u3068\u578B\u3092\u53D6\u5F97
|
|
558
585
|
3. \u5FC5\u8981\u306B\u5FDC\u3058\u3066\u30BD\u30FC\u30B9\u3084\u30EA\u30CD\u30FC\u30B8\u3092\u63A2\u7D22\u3057\u3001\u30C7\u30FC\u30BF\u69CB\u9020\u3092\u628A\u63E1`
|
|
559
586
|
}
|
|
560
587
|
});
|
|
@@ -582,10 +609,54 @@ function adminApiFetch(params, path2, init) {
|
|
|
582
609
|
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
583
610
|
return fetch(`${adminApiBase(host)}${trimmedPath}`, { ...init, headers });
|
|
584
611
|
}
|
|
612
|
+
function discoveryEndpoint(host) {
|
|
613
|
+
if (host.includes("emea")) return "https://metadata.emea.dbt.com/graphql";
|
|
614
|
+
if (host.includes(".au.")) return "https://metadata.au.dbt.com/graphql";
|
|
615
|
+
return "https://metadata.cloud.getdbt.com/graphql";
|
|
616
|
+
}
|
|
617
|
+
async function discoveryQuery(params, query, variables) {
|
|
618
|
+
const host = params[parameters.host.slug];
|
|
619
|
+
const token = params[parameters.token.slug];
|
|
620
|
+
const prodEnvId = params[parameters.prodEnvId.slug];
|
|
621
|
+
if (!host || !token || !prodEnvId) return null;
|
|
622
|
+
try {
|
|
623
|
+
const res = await fetch(discoveryEndpoint(host), {
|
|
624
|
+
method: "POST",
|
|
625
|
+
headers: {
|
|
626
|
+
"Content-Type": "application/json",
|
|
627
|
+
Authorization: `Bearer ${token}`,
|
|
628
|
+
Accept: "application/json"
|
|
629
|
+
},
|
|
630
|
+
body: JSON.stringify({
|
|
631
|
+
query,
|
|
632
|
+
variables: { environmentId: Number(prodEnvId), ...variables }
|
|
633
|
+
})
|
|
634
|
+
});
|
|
635
|
+
if (!res.ok) return null;
|
|
636
|
+
const json = await res.json();
|
|
637
|
+
if (json.errors && json.errors.length > 0) return null;
|
|
638
|
+
return json.data ?? null;
|
|
639
|
+
} catch {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
585
643
|
|
|
586
644
|
// ../connectors/src/connectors/dbt/setup-flow.ts
|
|
587
645
|
var ALL_PROJECTS = "__ALL_PROJECTS__";
|
|
588
646
|
var DBT_SETUP_MAX_PROJECTS = 10;
|
|
647
|
+
var DISCOVERY_FETCH_LIMIT = 500;
|
|
648
|
+
var MART_TIER1_DETAIL = 20;
|
|
649
|
+
var MART_TIER2_NAMES = 80;
|
|
650
|
+
var INTERMEDIATE_TIER1_DETAIL = 10;
|
|
651
|
+
var INTERMEDIATE_TIER2_NAMES = 40;
|
|
652
|
+
var OTHER_TIER1_DETAIL = 10;
|
|
653
|
+
var OTHER_TIER2_NAMES = 30;
|
|
654
|
+
var SOURCE_ENUMERATION_THRESHOLD = 25;
|
|
655
|
+
var MAX_EXPOSURE_DETAIL = 25;
|
|
656
|
+
var MAX_METRIC_DETAIL = 25;
|
|
657
|
+
var MAX_COLUMNS_PER_MODEL = 12;
|
|
658
|
+
var DESCRIPTION_MAX_PRIMARY = 180;
|
|
659
|
+
var DESCRIPTION_MAX_SECONDARY = 120;
|
|
589
660
|
async function listAllProjects(params) {
|
|
590
661
|
const accountId = params[parameters.accountId.slug];
|
|
591
662
|
if (!accountId) {
|
|
@@ -615,11 +686,556 @@ async function listAllProjects(params) {
|
|
|
615
686
|
return all;
|
|
616
687
|
}
|
|
617
688
|
function repoLabel(p) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return
|
|
689
|
+
return p.repository?.remote_url ?? p.repository?.name ?? "-";
|
|
690
|
+
}
|
|
691
|
+
async function fetchRecentRunCount(params, projectId) {
|
|
692
|
+
const accountId = params[parameters.accountId.slug];
|
|
693
|
+
if (!accountId) return null;
|
|
694
|
+
try {
|
|
695
|
+
const path2 = `/api/v2/accounts/${encodeURIComponent(accountId)}/runs/?project_id=${encodeURIComponent(projectId)}&limit=1&order_by=-id`;
|
|
696
|
+
const res = await adminApiFetch(params, path2);
|
|
697
|
+
if (!res.ok) return null;
|
|
698
|
+
const data = await res.json();
|
|
699
|
+
const total = data.extra?.pagination?.total_count;
|
|
700
|
+
return typeof total === "number" ? total : null;
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
var APPLIED_MODELS_QUERY = `query($environmentId: BigInt!, $first: Int!) {
|
|
706
|
+
environment(id: $environmentId) {
|
|
707
|
+
applied {
|
|
708
|
+
models(first: $first) {
|
|
709
|
+
totalCount
|
|
710
|
+
edges {
|
|
711
|
+
node {
|
|
712
|
+
uniqueId name description materializedType
|
|
713
|
+
database schema alias tags filePath packageName
|
|
714
|
+
catalog { columns { name type description } }
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}`;
|
|
721
|
+
var DEFINITION_MODELS_QUERY = `query($environmentId: BigInt!, $first: Int!) {
|
|
722
|
+
environment(id: $environmentId) {
|
|
723
|
+
definition {
|
|
724
|
+
models(first: $first) {
|
|
725
|
+
totalCount
|
|
726
|
+
edges {
|
|
727
|
+
node {
|
|
728
|
+
uniqueId name description materializedType
|
|
729
|
+
database schema alias tags filePath packageName
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}`;
|
|
736
|
+
var SOURCES_QUERY = `query($environmentId: BigInt!, $first: Int!) {
|
|
737
|
+
environment(id: $environmentId) {
|
|
738
|
+
applied {
|
|
739
|
+
sources(first: $first) {
|
|
740
|
+
totalCount
|
|
741
|
+
edges {
|
|
742
|
+
node {
|
|
743
|
+
uniqueId name description database schema identifier
|
|
744
|
+
sourceName freshness { freshnessStatus }
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}`;
|
|
751
|
+
var EXPOSURES_QUERY = `query($environmentId: BigInt!, $first: Int!) {
|
|
752
|
+
environment(id: $environmentId) {
|
|
753
|
+
applied {
|
|
754
|
+
exposures(first: $first) {
|
|
755
|
+
totalCount
|
|
756
|
+
edges {
|
|
757
|
+
node {
|
|
758
|
+
uniqueId name exposureType maturity label description url
|
|
759
|
+
ownerName ownerEmail tags
|
|
760
|
+
parents { uniqueId name }
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}`;
|
|
767
|
+
var METRICS_QUERY = `query($environmentId: BigInt!, $first: Int!) {
|
|
768
|
+
environment(id: $environmentId) {
|
|
769
|
+
definition {
|
|
770
|
+
metrics(first: $first) {
|
|
771
|
+
totalCount
|
|
772
|
+
edges {
|
|
773
|
+
node {
|
|
774
|
+
uniqueId name label description type filter formula
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}`;
|
|
781
|
+
async function fetchModels(params) {
|
|
782
|
+
const applied = await discoveryQuery(
|
|
783
|
+
params,
|
|
784
|
+
APPLIED_MODELS_QUERY,
|
|
785
|
+
{ first: DISCOVERY_FETCH_LIMIT }
|
|
786
|
+
);
|
|
787
|
+
const appliedNodes = applied?.environment?.applied?.models?.edges?.map((e) => e.node) ?? [];
|
|
788
|
+
if (appliedNodes.length > 0) {
|
|
789
|
+
return { models: appliedNodes, state: "applied" };
|
|
790
|
+
}
|
|
791
|
+
const definition = await discoveryQuery(
|
|
792
|
+
params,
|
|
793
|
+
DEFINITION_MODELS_QUERY,
|
|
794
|
+
{ first: DISCOVERY_FETCH_LIMIT }
|
|
795
|
+
);
|
|
796
|
+
const definitionNodes = definition?.environment?.definition?.models?.edges?.map((e) => e.node) ?? [];
|
|
797
|
+
if (definitionNodes.length > 0) {
|
|
798
|
+
return { models: definitionNodes, state: "definition" };
|
|
799
|
+
}
|
|
800
|
+
return { models: [], state: "empty" };
|
|
801
|
+
}
|
|
802
|
+
async function fetchSources(params) {
|
|
803
|
+
const data = await discoveryQuery(params, SOURCES_QUERY, { first: DISCOVERY_FETCH_LIMIT });
|
|
804
|
+
return data?.environment?.applied?.sources?.edges?.map((e) => e.node) ?? [];
|
|
805
|
+
}
|
|
806
|
+
async function fetchExposures(params) {
|
|
807
|
+
const data = await discoveryQuery(params, EXPOSURES_QUERY, { first: DISCOVERY_FETCH_LIMIT });
|
|
808
|
+
return data?.environment?.applied?.exposures?.edges?.map((e) => e.node) ?? [];
|
|
809
|
+
}
|
|
810
|
+
async function fetchMetrics(params) {
|
|
811
|
+
const data = await discoveryQuery(params, METRICS_QUERY, { first: DISCOVERY_FETCH_LIMIT });
|
|
812
|
+
return data?.environment?.definition?.metrics?.edges?.map((e) => e.node) ?? [];
|
|
813
|
+
}
|
|
814
|
+
var MART_TAG_SET = /* @__PURE__ */ new Set([
|
|
815
|
+
"mart",
|
|
816
|
+
"marts",
|
|
817
|
+
"gold",
|
|
818
|
+
"final",
|
|
819
|
+
"reporting",
|
|
820
|
+
"presentation",
|
|
821
|
+
"bi",
|
|
822
|
+
"analytics",
|
|
823
|
+
"business",
|
|
824
|
+
"datamart",
|
|
825
|
+
"dm",
|
|
826
|
+
"dwh",
|
|
827
|
+
"exposed",
|
|
828
|
+
"kpi",
|
|
829
|
+
"fact",
|
|
830
|
+
"dimension"
|
|
831
|
+
]);
|
|
832
|
+
var INTERMEDIATE_TAG_SET = /* @__PURE__ */ new Set([
|
|
833
|
+
"intermediate",
|
|
834
|
+
"int",
|
|
835
|
+
"silver",
|
|
836
|
+
"transform",
|
|
837
|
+
"transformed",
|
|
838
|
+
"derived",
|
|
839
|
+
"refined",
|
|
840
|
+
"prep",
|
|
841
|
+
"normalized"
|
|
842
|
+
]);
|
|
843
|
+
var STAGING_TAG_SET = /* @__PURE__ */ new Set([
|
|
844
|
+
"staging",
|
|
845
|
+
"stg",
|
|
846
|
+
"bronze",
|
|
847
|
+
"source",
|
|
848
|
+
"sources",
|
|
849
|
+
"raw",
|
|
850
|
+
"base",
|
|
851
|
+
"ingest",
|
|
852
|
+
"ingested",
|
|
853
|
+
"ods",
|
|
854
|
+
"landing"
|
|
855
|
+
]);
|
|
856
|
+
var MART_PATH_RE = /\/(marts?|gold|presentation|bi|reporting|analytics|datamart|dwh|dm|exposed|final|serve|serving)\//;
|
|
857
|
+
var INT_PATH_RE = /\/(intermediate|silver|transform|transformed|derived|refined|prep|preprocess|normalized)\//;
|
|
858
|
+
var STG_PATH_RE = /\/(staging|bronze|sources?|raw|ingest|ods|landing|base)\//;
|
|
859
|
+
var MART_NAME_RE = /^(fct|fact|dim|dimension|agg|mart|rpt|report|kpi|metric|cube|wide|obt|gold|datamart|dm|dwh|presentation|bi|final)_/;
|
|
860
|
+
var INT_NAME_RE = /^(int|intermediate|silver|derived|transform|refined|prep)_/;
|
|
861
|
+
var STG_NAME_RE = /^(stg|staging|src|source|raw|base|bronze|ingest|ods|landing)_/;
|
|
862
|
+
var MART_SCHEMA_RE = /^(mart|marts|analytics|reporting|presentation|datamart|dwh|dm|gold|bi)$|_(mart|marts|gold|datamart|dm|dwh|reporting|presentation)$/;
|
|
863
|
+
var INT_SCHEMA_RE = /^(int|intermediate|silver)$|_(intermediate|silver)$/;
|
|
864
|
+
var STG_SCHEMA_RE = /^(stg|staging|raw|sources?|bronze|base|ods|ingest|landing)$|_(staging|stg|raw|bronze|base|ods|landing)$/;
|
|
865
|
+
var LAYER_FOLDER_TOKENS = /* @__PURE__ */ new Set([
|
|
866
|
+
// standard
|
|
867
|
+
"marts",
|
|
868
|
+
"mart",
|
|
869
|
+
"intermediate",
|
|
870
|
+
"staging",
|
|
871
|
+
"sources",
|
|
872
|
+
"source",
|
|
873
|
+
"base",
|
|
874
|
+
// medallion
|
|
875
|
+
"bronze",
|
|
876
|
+
"silver",
|
|
877
|
+
"gold",
|
|
878
|
+
// dbt Labs newer
|
|
879
|
+
"presentation",
|
|
880
|
+
"transform",
|
|
881
|
+
// DW vocab
|
|
882
|
+
"reporting",
|
|
883
|
+
"analytics",
|
|
884
|
+
"datamart",
|
|
885
|
+
"dwh",
|
|
886
|
+
"dm",
|
|
887
|
+
"ods",
|
|
888
|
+
"raw",
|
|
889
|
+
"ingest",
|
|
890
|
+
"landing",
|
|
891
|
+
"final",
|
|
892
|
+
"exposed"
|
|
893
|
+
]);
|
|
894
|
+
function martLikelihoodScore(m) {
|
|
895
|
+
const mat = (m.materializedType ?? "").toLowerCase();
|
|
896
|
+
if (mat === "incremental") return 3;
|
|
897
|
+
if (mat === "table") return 3;
|
|
898
|
+
if (mat === "materialized_view") return 2;
|
|
899
|
+
if (mat === "view") return 1;
|
|
900
|
+
if (mat === "ephemeral") return 0;
|
|
901
|
+
if (mat === "seed") return 0;
|
|
902
|
+
return 1;
|
|
903
|
+
}
|
|
904
|
+
function layerModels(models, exposures) {
|
|
905
|
+
const layerOf = /* @__PURE__ */ new Map();
|
|
906
|
+
const byId = /* @__PURE__ */ new Map();
|
|
907
|
+
for (const m of models) {
|
|
908
|
+
const initial = classifyModel(m);
|
|
909
|
+
if (m.uniqueId) {
|
|
910
|
+
layerOf.set(m.uniqueId, initial);
|
|
911
|
+
byId.set(m.uniqueId, m);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
for (const e of exposures) {
|
|
915
|
+
for (const p of e.parents ?? []) {
|
|
916
|
+
if (!p.uniqueId) continue;
|
|
917
|
+
const current = layerOf.get(p.uniqueId);
|
|
918
|
+
if (current === "other" || current === "intermediate") {
|
|
919
|
+
layerOf.set(p.uniqueId, "mart");
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const out = {
|
|
924
|
+
mart: [],
|
|
925
|
+
intermediate: [],
|
|
926
|
+
staging: [],
|
|
927
|
+
other: []
|
|
928
|
+
};
|
|
929
|
+
for (const m of models) {
|
|
930
|
+
const layer = m.uniqueId && layerOf.get(m.uniqueId) || classifyModel(m);
|
|
931
|
+
out[layer].push(m);
|
|
932
|
+
}
|
|
933
|
+
return out;
|
|
934
|
+
}
|
|
935
|
+
function classifyByTags(tags) {
|
|
936
|
+
for (const raw of tags) {
|
|
937
|
+
const t = raw.toLowerCase();
|
|
938
|
+
if (MART_TAG_SET.has(t)) return "mart";
|
|
939
|
+
if (INTERMEDIATE_TAG_SET.has(t)) return "intermediate";
|
|
940
|
+
if (STAGING_TAG_SET.has(t)) return "staging";
|
|
941
|
+
}
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
function classifyModel(m) {
|
|
945
|
+
const fromTags = classifyByTags(m.tags ?? []);
|
|
946
|
+
if (fromTags) return fromTags;
|
|
947
|
+
const path2 = (m.filePath ?? "").toLowerCase();
|
|
948
|
+
if (path2) {
|
|
949
|
+
if (MART_PATH_RE.test(path2)) return "mart";
|
|
950
|
+
if (INT_PATH_RE.test(path2)) return "intermediate";
|
|
951
|
+
if (STG_PATH_RE.test(path2)) return "staging";
|
|
952
|
+
}
|
|
953
|
+
const name = (m.name ?? "").toLowerCase();
|
|
954
|
+
if (MART_NAME_RE.test(name)) return "mart";
|
|
955
|
+
if (INT_NAME_RE.test(name)) return "intermediate";
|
|
956
|
+
if (STG_NAME_RE.test(name)) return "staging";
|
|
957
|
+
const schema = (m.schema ?? "").toLowerCase();
|
|
958
|
+
if (schema) {
|
|
959
|
+
if (MART_SCHEMA_RE.test(schema)) return "mart";
|
|
960
|
+
if (INT_SCHEMA_RE.test(schema)) return "intermediate";
|
|
961
|
+
if (STG_SCHEMA_RE.test(schema)) return "staging";
|
|
962
|
+
}
|
|
963
|
+
return "other";
|
|
964
|
+
}
|
|
965
|
+
function modelGroupFolder(m) {
|
|
966
|
+
const fp = (m.filePath ?? "").toLowerCase();
|
|
967
|
+
const cleaned = fp.replace(/^models\//, "");
|
|
968
|
+
const segments = cleaned ? cleaned.split("/") : [];
|
|
969
|
+
while (segments.length > 0 && LAYER_FOLDER_TOKENS.has(segments[0])) {
|
|
970
|
+
segments.shift();
|
|
971
|
+
}
|
|
972
|
+
if (segments.length >= 2) return segments[0];
|
|
973
|
+
if (m.schema) return m.schema.toLowerCase();
|
|
974
|
+
const namePrefix = (m.name ?? "").split("_")[0];
|
|
975
|
+
if (namePrefix && namePrefix !== m.name) return namePrefix;
|
|
976
|
+
return "(ungrouped)";
|
|
977
|
+
}
|
|
978
|
+
function escapePipe(s) {
|
|
979
|
+
return s.replace(/\|/g, "\\|");
|
|
980
|
+
}
|
|
981
|
+
function summarizeDescription(d, max = 200) {
|
|
982
|
+
if (!d) return null;
|
|
983
|
+
const collapsed = d.replace(/\s+/g, " ").trim();
|
|
984
|
+
if (!collapsed) return null;
|
|
985
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
986
|
+
}
|
|
987
|
+
function countBy(items, key) {
|
|
988
|
+
const m = /* @__PURE__ */ new Map();
|
|
989
|
+
for (const it of items) {
|
|
990
|
+
const k = key(it);
|
|
991
|
+
m.set(k, (m.get(k) ?? 0) + 1);
|
|
992
|
+
}
|
|
993
|
+
return m;
|
|
994
|
+
}
|
|
995
|
+
function sortedEntries(m) {
|
|
996
|
+
return Array.from(m.entries()).sort(
|
|
997
|
+
(a, b) => b[1] - a[1] || a[0].localeCompare(b[0])
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
function modelIdentifier(m) {
|
|
1001
|
+
const parts = [m.schema, m.alias ?? m.name].filter(Boolean);
|
|
1002
|
+
return parts.join(".") || m.name || m.uniqueId || "(unknown)";
|
|
1003
|
+
}
|
|
1004
|
+
function renderInventoryOverview(sections, models, modelState, sources, exposures, metrics, layered) {
|
|
1005
|
+
sections.push("### Inventory overview", "");
|
|
1006
|
+
if (models.length === 0) {
|
|
1007
|
+
sections.push(
|
|
1008
|
+
"_The pinned production environment has no models surfaced by dbt's Discovery API. Trigger a successful run (or verify the environment ID) before generating dashboards._",
|
|
1009
|
+
""
|
|
1010
|
+
);
|
|
1011
|
+
} else {
|
|
1012
|
+
const materializations = countBy(
|
|
1013
|
+
models,
|
|
1014
|
+
(m) => (m.materializedType ?? "unknown").toLowerCase()
|
|
1015
|
+
);
|
|
1016
|
+
const layerOrder = [
|
|
1017
|
+
"mart",
|
|
1018
|
+
"intermediate",
|
|
1019
|
+
"staging",
|
|
1020
|
+
"other"
|
|
1021
|
+
];
|
|
1022
|
+
const layerSummary = layered ? layerOrder.map((l) => [l, layered[l].length]).filter(([, n]) => n > 0).map(([l, n]) => `${l} ${n}`).join(" \xB7 ") : "";
|
|
1023
|
+
const matSummary = sortedEntries(materializations).map(([k, n]) => `${k} ${n}`).join(" \xB7 ");
|
|
1024
|
+
const stateNote = modelState === "applied" ? "from latest successful prod run (columns available)" : "from project definition (no prod run yet \u2014 columns unavailable)";
|
|
1025
|
+
sections.push(`- Models: **${models.length}** _(${stateNote})_`);
|
|
1026
|
+
if (layerSummary) sections.push(` - By layer: ${layerSummary}`);
|
|
1027
|
+
if (matSummary) sections.push(` - By materialization: ${matSummary}`);
|
|
1028
|
+
}
|
|
1029
|
+
if (sources.length > 0) {
|
|
1030
|
+
const bySource = countBy(sources, (s) => s.sourceName ?? "(unnamed)");
|
|
1031
|
+
const summary = sortedEntries(bySource).map(([k, n]) => `${k} ${n}`).join(" \xB7 ");
|
|
1032
|
+
sections.push(`- Sources: **${sources.length}** \u2014 ${summary}`);
|
|
1033
|
+
} else {
|
|
1034
|
+
sections.push(`- Sources: 0`);
|
|
1035
|
+
}
|
|
1036
|
+
sections.push(
|
|
1037
|
+
`- Exposures: **${exposures.length}**${exposures.length === 0 ? " _(no BI dashboards declared in dbt yet)_" : ""}`
|
|
1038
|
+
);
|
|
1039
|
+
sections.push(
|
|
1040
|
+
`- Metrics: **${metrics.length}**${metrics.length === 0 ? " _(no semantic-layer metrics defined \u2014 aggregations need to be derived from models)_" : ""}`
|
|
1041
|
+
);
|
|
1042
|
+
sections.push("");
|
|
1043
|
+
}
|
|
1044
|
+
function renderModelLayerTiered(sections, layerLabel, blurb, models, tier1, tier2, opts) {
|
|
1045
|
+
if (models.length === 0) return;
|
|
1046
|
+
sections.push(`### ${layerLabel} (${models.length} models)`, "", blurb, "");
|
|
1047
|
+
const renderDetailed = (m) => {
|
|
1048
|
+
const id = modelIdentifier(m);
|
|
1049
|
+
const mat = m.materializedType ? ` _(${m.materializedType})_` : "";
|
|
1050
|
+
const desc = summarizeDescription(m.description, DESCRIPTION_MAX_PRIMARY);
|
|
1051
|
+
sections.push(`- \`${id}\`${mat}${desc ? ` \u2014 ${desc}` : ""}`);
|
|
1052
|
+
if (opts.showColumns) {
|
|
1053
|
+
const cols = m.catalog?.columns ?? [];
|
|
1054
|
+
if (cols.length > 0) {
|
|
1055
|
+
const rendered = cols.slice(0, MAX_COLUMNS_PER_MODEL).map((c) => c.type ? `${c.name} (${c.type})` : `${c.name}`).join(", ");
|
|
1056
|
+
const more = cols.length > MAX_COLUMNS_PER_MODEL ? ` (+${cols.length - MAX_COLUMNS_PER_MODEL} more)` : "";
|
|
1057
|
+
sections.push(` - Columns: ${rendered}${more}`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
const detailedSelected = /* @__PURE__ */ new Set();
|
|
1062
|
+
if (opts.group) {
|
|
1063
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1064
|
+
for (const m of models) {
|
|
1065
|
+
const k = modelGroupFolder(m);
|
|
1066
|
+
if (!buckets.has(k)) buckets.set(k, []);
|
|
1067
|
+
buckets.get(k).push(m);
|
|
1068
|
+
}
|
|
1069
|
+
const sortedBuckets = Array.from(buckets.entries()).sort(
|
|
1070
|
+
(a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])
|
|
1071
|
+
);
|
|
1072
|
+
const useHeadings = sortedBuckets.length > 1;
|
|
1073
|
+
const perFolderCap = Math.max(
|
|
1074
|
+
2,
|
|
1075
|
+
Math.ceil(tier1 / Math.max(1, sortedBuckets.length))
|
|
1076
|
+
);
|
|
1077
|
+
let budget = tier1;
|
|
1078
|
+
for (const [folder, bucket] of sortedBuckets) {
|
|
1079
|
+
if (useHeadings) {
|
|
1080
|
+
sections.push(`#### ${folder} (${bucket.length})`);
|
|
1081
|
+
}
|
|
1082
|
+
const take = Math.min(bucket.length, perFolderCap, budget);
|
|
1083
|
+
const detailedHere = bucket.slice(0, take);
|
|
1084
|
+
for (const m of detailedHere) {
|
|
1085
|
+
renderDetailed(m);
|
|
1086
|
+
detailedSelected.add(m);
|
|
1087
|
+
}
|
|
1088
|
+
budget -= detailedHere.length;
|
|
1089
|
+
if (bucket.length > detailedHere.length && useHeadings) {
|
|
1090
|
+
sections.push(
|
|
1091
|
+
`- _\u2026and ${bucket.length - detailedHere.length} more in this folder._`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
sections.push("");
|
|
1095
|
+
}
|
|
1096
|
+
} else {
|
|
1097
|
+
const detailed = models.slice(0, tier1);
|
|
1098
|
+
for (const m of detailed) {
|
|
1099
|
+
renderDetailed(m);
|
|
1100
|
+
detailedSelected.add(m);
|
|
1101
|
+
}
|
|
1102
|
+
if (detailed.length > 0) sections.push("");
|
|
1103
|
+
}
|
|
1104
|
+
const remaining = models.filter((m) => !detailedSelected.has(m));
|
|
1105
|
+
const named = remaining.slice(0, tier2);
|
|
1106
|
+
const overflow = remaining.length - named.length;
|
|
1107
|
+
if (named.length > 0) {
|
|
1108
|
+
sections.push(
|
|
1109
|
+
`_Also available (name only \u2014 ask via the dbt request tool for details):_`
|
|
1110
|
+
);
|
|
1111
|
+
sections.push(named.map((m) => `\`${m.name}\``).join(", "), "");
|
|
1112
|
+
}
|
|
1113
|
+
if (overflow > 0) {
|
|
1114
|
+
sections.push(
|
|
1115
|
+
`_\u2026and ${overflow} more ${layerLabel.toLowerCase()} models not surfaced here. The dbt request tool can enumerate them on demand._`,
|
|
1116
|
+
""
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function renderStagingLayer(sections, models) {
|
|
1121
|
+
if (models.length === 0) return;
|
|
1122
|
+
sections.push(
|
|
1123
|
+
`### Staging layer (${models.length} models)`,
|
|
1124
|
+
"",
|
|
1125
|
+
"_Source-aligned cleanups (one staging model \u2248 one raw table). Almost never the right pick for dashboards \u2014 use mart or intermediate instead. Listed by group count only to keep this summary compact; the dbt request tool can enumerate individual names if needed._",
|
|
1126
|
+
""
|
|
1127
|
+
);
|
|
1128
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1129
|
+
for (const m of models) {
|
|
1130
|
+
const k = modelGroupFolder(m);
|
|
1131
|
+
if (!groups.has(k)) groups.set(k, []);
|
|
1132
|
+
groups.get(k).push(m);
|
|
1133
|
+
}
|
|
1134
|
+
const entries = Array.from(groups.entries()).sort(
|
|
1135
|
+
(a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])
|
|
1136
|
+
);
|
|
1137
|
+
for (const [folder, bucket] of entries) {
|
|
1138
|
+
sections.push(`- **${folder}**: ${bucket.length}`);
|
|
1139
|
+
}
|
|
1140
|
+
sections.push("");
|
|
1141
|
+
}
|
|
1142
|
+
function renderExposures(sections, exposures) {
|
|
1143
|
+
if (exposures.length === 0) return;
|
|
1144
|
+
sections.push(
|
|
1145
|
+
`### Exposures (existing dashboards / downstream apps) \u2014 ${exposures.length}`,
|
|
1146
|
+
"",
|
|
1147
|
+
"_Already declared by the team. Treat them as ground truth for what stakeholders care about; avoid proposing exact duplicates._",
|
|
1148
|
+
""
|
|
1149
|
+
);
|
|
1150
|
+
for (const e of exposures.slice(0, MAX_EXPOSURE_DETAIL)) {
|
|
1151
|
+
const title = e.label || e.name || e.uniqueId || "(unknown)";
|
|
1152
|
+
const meta = [
|
|
1153
|
+
e.exposureType ? `type: ${e.exposureType}` : null,
|
|
1154
|
+
e.maturity ? `maturity: ${e.maturity}` : null
|
|
1155
|
+
].filter(Boolean).join(", ");
|
|
1156
|
+
sections.push(`- **${title}**${meta ? ` (${meta})` : ""}`);
|
|
1157
|
+
const desc = summarizeDescription(e.description, DESCRIPTION_MAX_SECONDARY);
|
|
1158
|
+
if (desc) sections.push(` - ${desc}`);
|
|
1159
|
+
if (e.url) sections.push(` - URL: ${e.url}`);
|
|
1160
|
+
const parents = (e.parents ?? []).map((p) => p.name).filter((n) => !!n).slice(0, 6);
|
|
1161
|
+
if (parents.length > 0) {
|
|
1162
|
+
sections.push(` - Built on: ${parents.join(", ")}`);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (exposures.length > MAX_EXPOSURE_DETAIL) {
|
|
1166
|
+
sections.push(
|
|
1167
|
+
`- _\u2026and ${exposures.length - MAX_EXPOSURE_DETAIL} more \u2014 fetch via the dbt request tool._`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
sections.push("");
|
|
1171
|
+
}
|
|
1172
|
+
function renderMetrics(sections, metrics) {
|
|
1173
|
+
if (metrics.length === 0) return;
|
|
1174
|
+
sections.push(
|
|
1175
|
+
`### Metrics \u2014 ${metrics.length}`,
|
|
1176
|
+
"",
|
|
1177
|
+
"_Semantic-layer definitions vetted by the team. Wire them straight into KPI tiles instead of re-deriving the same aggregation in a chart._",
|
|
1178
|
+
""
|
|
1179
|
+
);
|
|
1180
|
+
for (const m of metrics.slice(0, MAX_METRIC_DETAIL)) {
|
|
1181
|
+
const title = m.label || m.name || m.uniqueId || "(unknown)";
|
|
1182
|
+
const meta = m.type ? ` _(type: ${m.type})_` : "";
|
|
1183
|
+
sections.push(`- **${title}**${meta}`);
|
|
1184
|
+
const desc = summarizeDescription(m.description, DESCRIPTION_MAX_SECONDARY);
|
|
1185
|
+
if (desc) sections.push(` - ${desc}`);
|
|
1186
|
+
if (m.formula) sections.push(` - Formula: \`${m.formula}\``);
|
|
1187
|
+
if (m.filter) sections.push(` - Filter: \`${m.filter}\``);
|
|
1188
|
+
}
|
|
1189
|
+
if (metrics.length > MAX_METRIC_DETAIL) {
|
|
1190
|
+
sections.push(
|
|
1191
|
+
`- _\u2026and ${metrics.length - MAX_METRIC_DETAIL} more \u2014 fetch via the dbt request tool._`
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
sections.push("");
|
|
1195
|
+
}
|
|
1196
|
+
function renderSources(sections, sources) {
|
|
1197
|
+
if (sources.length === 0) return;
|
|
1198
|
+
sections.push(
|
|
1199
|
+
`### Sources (${sources.length})`,
|
|
1200
|
+
"",
|
|
1201
|
+
"_Upstream raw tables feeding the staging layer. Reference them for lineage / freshness, not for direct dashboard reads._",
|
|
1202
|
+
""
|
|
1203
|
+
);
|
|
1204
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const s of sources) {
|
|
1206
|
+
const k = s.sourceName ?? "(unnamed)";
|
|
1207
|
+
if (!grouped.has(k)) grouped.set(k, []);
|
|
1208
|
+
grouped.get(k).push(s);
|
|
1209
|
+
}
|
|
1210
|
+
const entries = Array.from(grouped.entries()).sort(
|
|
1211
|
+
(a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])
|
|
1212
|
+
);
|
|
1213
|
+
const enumerateNames = sources.length <= SOURCE_ENUMERATION_THRESHOLD;
|
|
1214
|
+
for (const [src, bucket] of entries) {
|
|
1215
|
+
const warehouse = bucket[0]?.database && bucket[0]?.schema ? ` \u2014 \`${bucket[0].database}.${bucket[0].schema}\`` : "";
|
|
1216
|
+
if (enumerateNames) {
|
|
1217
|
+
const names = bucket.map((s) => `\`${s.name}\``).join(", ");
|
|
1218
|
+
sections.push(`- **${src}** (${bucket.length})${warehouse}: ${names}`);
|
|
1219
|
+
} else {
|
|
1220
|
+
sections.push(`- **${src}** (${bucket.length})${warehouse}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (!enumerateNames) {
|
|
1224
|
+
sections.push(
|
|
1225
|
+
"",
|
|
1226
|
+
`_Per-source table names omitted because the total exceeds ${SOURCE_ENUMERATION_THRESHOLD}. Use the dbt request tool to enumerate individual sources._`
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
const freshnessIssues = sources.filter(
|
|
1230
|
+
(s) => s.freshness?.freshnessStatus && !["Pass", "Unconfigured"].includes(s.freshness.freshnessStatus)
|
|
1231
|
+
);
|
|
1232
|
+
if (freshnessIssues.length > 0) {
|
|
1233
|
+
sections.push(
|
|
1234
|
+
"",
|
|
1235
|
+
`> \u26A0\uFE0F ${freshnessIssues.length} source(s) failing freshness \u2014 downstream metrics may be stale.`
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
sections.push("");
|
|
623
1239
|
}
|
|
624
1240
|
var dbtSetupFlow = {
|
|
625
1241
|
initialState: () => ({}),
|
|
@@ -651,7 +1267,7 @@ var dbtSetupFlow = {
|
|
|
651
1267
|
throw new Error("dbt setup: incomplete state on finalize");
|
|
652
1268
|
}
|
|
653
1269
|
const allProjects = await listAllProjects(rt.params);
|
|
654
|
-
const
|
|
1270
|
+
const projectById = new Map(allProjects.map((p) => [String(p.id), p]));
|
|
655
1271
|
const targetIds = await resolveSetupSelection({
|
|
656
1272
|
selected: state.projects,
|
|
657
1273
|
allSentinel: ALL_PROJECTS,
|
|
@@ -662,25 +1278,97 @@ var dbtSetupFlow = {
|
|
|
662
1278
|
const accountId = rt.params[parameters.accountId.slug];
|
|
663
1279
|
const prodEnvId = rt.params[parameters.prodEnvId.slug];
|
|
664
1280
|
if (accountId) sections.push(`- Account: \`${accountId}\``);
|
|
665
|
-
if (prodEnvId) sections.push(`-
|
|
666
|
-
sections.push(
|
|
667
|
-
|
|
1281
|
+
if (prodEnvId) sections.push(`- Production environment: \`${prodEnvId}\``);
|
|
1282
|
+
sections.push(
|
|
1283
|
+
"",
|
|
1284
|
+
"_The downstream coding agent should use this summary as the canonical map of dbt assets for dashboard / app proposals. Reach for the mart layer first; fall back to intermediate models for custom cuts; consult sources only for lineage._",
|
|
1285
|
+
""
|
|
1286
|
+
);
|
|
1287
|
+
if (targetIds.length === 0) {
|
|
668
1288
|
sections.push("_No projects selected._", "");
|
|
669
1289
|
return sections.join("\n");
|
|
670
1290
|
}
|
|
671
|
-
sections.push("
|
|
672
|
-
sections.push("
|
|
673
|
-
|
|
674
|
-
|
|
1291
|
+
sections.push("### Selected projects", "");
|
|
1292
|
+
sections.push("| Project | Repository | Recent runs |");
|
|
1293
|
+
sections.push("|---------|------------|-------------|");
|
|
1294
|
+
const runCounts = await Promise.all(
|
|
1295
|
+
targetIds.map((id) => fetchRecentRunCount(rt.params, id))
|
|
1296
|
+
);
|
|
1297
|
+
for (let i = 0; i < targetIds.length; i++) {
|
|
1298
|
+
const id = targetIds[i];
|
|
1299
|
+
const p = projectById.get(id);
|
|
1300
|
+
const runs = runCounts[i] == null ? "-" : String(runCounts[i]);
|
|
675
1301
|
if (!p) {
|
|
676
|
-
sections.push(`| ${id} | - |`);
|
|
1302
|
+
sections.push(`| ${id} | - | ${runs} |`);
|
|
677
1303
|
continue;
|
|
678
1304
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1305
|
+
sections.push(
|
|
1306
|
+
`| ${escapePipe(p.name)} | ${escapePipe(repoLabel(p))} | ${runs} |`
|
|
1307
|
+
);
|
|
682
1308
|
}
|
|
683
1309
|
sections.push("");
|
|
1310
|
+
const [{ models, state: modelState }, sources, exposures, metrics] = await Promise.all([
|
|
1311
|
+
fetchModels(rt.params),
|
|
1312
|
+
fetchSources(rt.params),
|
|
1313
|
+
fetchExposures(rt.params),
|
|
1314
|
+
fetchMetrics(rt.params)
|
|
1315
|
+
]);
|
|
1316
|
+
const layered = models.length > 0 ? layerModels(models, exposures) : null;
|
|
1317
|
+
renderInventoryOverview(
|
|
1318
|
+
sections,
|
|
1319
|
+
models,
|
|
1320
|
+
modelState,
|
|
1321
|
+
sources,
|
|
1322
|
+
exposures,
|
|
1323
|
+
metrics,
|
|
1324
|
+
layered
|
|
1325
|
+
);
|
|
1326
|
+
if (layered) {
|
|
1327
|
+
const byMartLikelihood = (a, b) => martLikelihoodScore(b) - martLikelihoodScore(a) || (a.name ?? "").localeCompare(b.name ?? "");
|
|
1328
|
+
const byName = (a, b) => (a.name ?? "").localeCompare(b.name ?? "");
|
|
1329
|
+
layered.mart.sort(byMartLikelihood);
|
|
1330
|
+
layered.other.sort(byMartLikelihood);
|
|
1331
|
+
layered.intermediate.sort(byName);
|
|
1332
|
+
layered.staging.sort(byName);
|
|
1333
|
+
renderExposures(sections, exposures);
|
|
1334
|
+
renderMetrics(sections, metrics);
|
|
1335
|
+
const martLabel = layered.mart.length === 0 && layered.other.length > 0 ? "Models (no layer convention detected \u2014 treat as candidate dashboard inputs)" : "Mart layer \u2014 start here for dashboards";
|
|
1336
|
+
const martBlurb = layered.mart.length === 0 && layered.other.length > 0 ? "_This project doesn't follow the staging/intermediate/marts (or bronze/silver/gold) convention, so layer inference fell back to surfacing every non-staging model here. Use descriptions, columns, and materialization (`table`/`incremental` are stronger dashboard candidates than `view`) to pick the right inputs._" : "_Curated, business-facing tables. Prefer them as the primary input for new dashboards and apps. Reach for the intermediate layer only when a mart model is missing the cut you need._";
|
|
1337
|
+
const martModels = layered.mart.length === 0 && layered.other.length > 0 ? layered.other : layered.mart;
|
|
1338
|
+
renderModelLayerTiered(
|
|
1339
|
+
sections,
|
|
1340
|
+
martLabel,
|
|
1341
|
+
martBlurb,
|
|
1342
|
+
martModels,
|
|
1343
|
+
MART_TIER1_DETAIL,
|
|
1344
|
+
MART_TIER2_NAMES,
|
|
1345
|
+
{ showColumns: modelState === "applied", group: true }
|
|
1346
|
+
);
|
|
1347
|
+
renderModelLayerTiered(
|
|
1348
|
+
sections,
|
|
1349
|
+
"Intermediate layer",
|
|
1350
|
+
"_Joined / derived models used to build the mart layer. Useful when you need a calculation the mart layer doesn't expose; avoid wiring them directly into a dashboard if a mart model already covers the use case._",
|
|
1351
|
+
layered.intermediate,
|
|
1352
|
+
INTERMEDIATE_TIER1_DETAIL,
|
|
1353
|
+
INTERMEDIATE_TIER2_NAMES,
|
|
1354
|
+
{ showColumns: false, group: false }
|
|
1355
|
+
);
|
|
1356
|
+
const otherForOwnSection = layered.mart.length > 0 ? layered.other : [];
|
|
1357
|
+
renderModelLayerTiered(
|
|
1358
|
+
sections,
|
|
1359
|
+
"Other models",
|
|
1360
|
+
"_Models that don't match the staging / intermediate / mart convention. Inspect case-by-case before reusing._",
|
|
1361
|
+
otherForOwnSection,
|
|
1362
|
+
OTHER_TIER1_DETAIL,
|
|
1363
|
+
OTHER_TIER2_NAMES,
|
|
1364
|
+
{ showColumns: false, group: false }
|
|
1365
|
+
);
|
|
1366
|
+
renderStagingLayer(sections, layered.staging);
|
|
1367
|
+
} else {
|
|
1368
|
+
renderExposures(sections, exposures);
|
|
1369
|
+
renderMetrics(sections, metrics);
|
|
1370
|
+
}
|
|
1371
|
+
renderSources(sections, sources);
|
|
684
1372
|
return sections.join("\n");
|
|
685
1373
|
}
|
|
686
1374
|
};
|
|
@@ -785,7 +1473,7 @@ var dbtConnector = new ConnectorPlugin({
|
|
|
785
1473
|
systemPrompt: {
|
|
786
1474
|
en: `### Tools
|
|
787
1475
|
|
|
788
|
-
- \`
|
|
1476
|
+
- \`connector_dbt_request\`: The only way to call the dbt Cloud Discovery API (GraphQL). Use it to explore models, sources, tests, metrics, columns, and lineage. The \`{environmentId}\` placeholder in GraphQL variables is automatically replaced with the prod-env-id. See the dbt Cloud Discovery API Reference below for available GraphQL queries.
|
|
789
1477
|
|
|
790
1478
|
### Business Logic
|
|
791
1479
|
|
|
@@ -894,7 +1582,7 @@ query($environmentId: BigInt!, $uniqueIds: [String!]!) {
|
|
|
894
1582
|
- Get \`ancestors { uniqueId name }\` or \`children { uniqueId name }\` within node`,
|
|
895
1583
|
ja: `### \u30C4\u30FC\u30EB
|
|
896
1584
|
|
|
897
|
-
- \`
|
|
1585
|
+
- \`connector_dbt_request\`: dbt Cloud Discovery API (GraphQL) \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002\u30E2\u30C7\u30EB\u3001\u30BD\u30FC\u30B9\u3001\u30C6\u30B9\u30C8\u3001\u30E1\u30C8\u30EA\u30AF\u30B9\u3001\u30AB\u30E9\u30E0\u3001\u30EA\u30CD\u30FC\u30B8\u306E\u63A2\u7D22\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002GraphQL\u5909\u6570\u5185\u306E \`{environmentId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u306Fprod-env-id\u3067\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002\u5229\u7528\u53EF\u80FD\u306AGraphQL\u30AF\u30A8\u30EA\u306F\u4E0B\u90E8\u306E\u300Cdbt Cloud Discovery API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
898
1586
|
|
|
899
1587
|
### Business Logic
|
|
900
1588
|
|