@squadbase/vite-server 0.1.17-dev.a9ddcfa → 0.1.17
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 +3281 -731
- package/dist/connectors/airtable-oauth.js +48 -8
- package/dist/connectors/airtable.js +44 -8
- package/dist/connectors/amplitude.js +8 -8
- package/dist/connectors/anthropic.js +2 -2
- package/dist/connectors/asana.js +37 -10
- package/dist/connectors/attio.js +30 -13
- package/dist/connectors/aws-billing.js +8 -8
- package/dist/connectors/azure-sql.js +31 -7
- package/dist/connectors/backlog-api-key.js +40 -15
- package/dist/connectors/clickup.js +50 -10
- package/dist/connectors/cosmosdb.js +12 -12
- package/dist/connectors/customerio.js +8 -8
- package/dist/connectors/dbt.js +686 -25
- package/dist/connectors/freshdesk.js +82 -8
- package/dist/connectors/freshsales.js +8 -8
- package/dist/connectors/freshservice.js +8 -8
- package/dist/connectors/gamma.js +15 -15
- package/dist/connectors/gemini.js +2 -2
- package/dist/connectors/github.js +12 -12
- package/dist/connectors/gmail-oauth.js +10 -10
- package/dist/connectors/gmail.js +4 -4
- package/dist/connectors/google-ads.js +8 -8
- package/dist/connectors/google-analytics-oauth.js +152 -25
- package/dist/connectors/google-analytics.js +490 -96
- package/dist/connectors/google-audit-log.js +4 -4
- package/dist/connectors/google-calendar-oauth.js +61 -15
- package/dist/connectors/google-calendar.js +61 -11
- package/dist/connectors/google-docs.js +10 -10
- package/dist/connectors/google-drive.js +32 -10
- package/dist/connectors/google-search-console-oauth.js +126 -17
- package/dist/connectors/google-sheets.js +6 -6
- package/dist/connectors/google-slides.js +10 -10
- package/dist/connectors/grafana.js +45 -10
- package/dist/connectors/hubspot-oauth.js +41 -9
- package/dist/connectors/hubspot.js +25 -9
- package/dist/connectors/influxdb.js +8 -8
- package/dist/connectors/intercom-oauth.js +72 -12
- package/dist/connectors/intercom.js +12 -12
- package/dist/connectors/jdbc.js +6 -6
- package/dist/connectors/jira-api-key.js +68 -11
- package/dist/connectors/kintone-api-token.js +66 -18
- package/dist/connectors/kintone.js +54 -11
- package/dist/connectors/linear.js +54 -12
- package/dist/connectors/linkedin-ads.js +41 -14
- package/dist/connectors/mailchimp-oauth.js +6 -6
- package/dist/connectors/mailchimp.js +6 -6
- package/dist/connectors/meta-ads-oauth.js +33 -14
- package/dist/connectors/meta-ads.js +35 -14
- package/dist/connectors/mixpanel.js +8 -8
- package/dist/connectors/monday.js +9 -9
- package/dist/connectors/mongodb.js +8 -8
- package/dist/connectors/notion-oauth.js +60 -11
- package/dist/connectors/notion.js +60 -11
- package/dist/connectors/openai.js +2 -2
- package/dist/connectors/oracle.js +23 -7
- package/dist/connectors/outlook-oauth.js +20 -20
- package/dist/connectors/powerbi-oauth.js +12 -12
- package/dist/connectors/salesforce.js +42 -9
- package/dist/connectors/semrush.js +6 -6
- package/dist/connectors/sentry.js +36 -10
- package/dist/connectors/shopify-oauth.js +43 -10
- package/dist/connectors/shopify.js +8 -8
- package/dist/connectors/sqlserver.js +31 -7
- package/dist/connectors/stripe-api-key.js +66 -15
- package/dist/connectors/stripe-oauth.js +70 -19
- package/dist/connectors/supabase.js +22 -5
- package/dist/connectors/tableau.js +14 -14
- package/dist/connectors/tiktok-ads.js +37 -16
- package/dist/connectors/wix-store.js +8 -8
- package/dist/connectors/zendesk-oauth.js +55 -12
- package/dist/connectors/zendesk.js +12 -12
- package/dist/index.js +3299 -729
- package/dist/main.js +3299 -729
- package/dist/vite-plugin.js +3279 -729
- package/package.json +1 -1
package/dist/connectors/dbt.js
CHANGED
|
@@ -437,7 +437,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
437
437
|
/**
|
|
438
438
|
* Create tools for connections that belong to this connector.
|
|
439
439
|
* Filters connections by connectorKey internally.
|
|
440
|
-
* Returns tools keyed as
|
|
440
|
+
* Returns tools keyed as `connector_${connectorKey}_${toolName}`.
|
|
441
441
|
*/
|
|
442
442
|
createTools(connections, config, opts) {
|
|
443
443
|
const myConnections = connections.filter(
|
|
@@ -447,7 +447,7 @@ var ConnectorPlugin = class _ConnectorPlugin {
|
|
|
447
447
|
for (const t of Object.values(this.tools)) {
|
|
448
448
|
const tool = t.createTool(myConnections, config);
|
|
449
449
|
const originalToModelOutput = tool.toModelOutput;
|
|
450
|
-
result[
|
|
450
|
+
result[`connector_${this.connectorKey}_${t.name}`] = {
|
|
451
451
|
...tool,
|
|
452
452
|
toModelOutput: async (options) => {
|
|
453
453
|
if (!originalToModelOutput) {
|
|
@@ -577,11 +577,11 @@ var AUTH_TYPES = {
|
|
|
577
577
|
// ../connectors/src/connectors/dbt/setup.ts
|
|
578
578
|
var dbtOnboarding = new ConnectorOnboarding({
|
|
579
579
|
dataOverviewInstructions: {
|
|
580
|
-
en: `1. Call
|
|
581
|
-
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
|
|
582
582
|
3. Explore sources and lineage as needed to understand the data structure`,
|
|
583
|
-
ja: `1.
|
|
584
|
-
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
|
|
585
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`
|
|
586
586
|
}
|
|
587
587
|
});
|
|
@@ -609,10 +609,54 @@ function adminApiFetch(params, path2, init) {
|
|
|
609
609
|
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
610
610
|
return fetch(`${adminApiBase(host)}${trimmedPath}`, { ...init, headers });
|
|
611
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
|
+
}
|
|
612
643
|
|
|
613
644
|
// ../connectors/src/connectors/dbt/setup-flow.ts
|
|
614
645
|
var ALL_PROJECTS = "__ALL_PROJECTS__";
|
|
615
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;
|
|
616
660
|
async function listAllProjects(params) {
|
|
617
661
|
const accountId = params[parameters.accountId.slug];
|
|
618
662
|
if (!accountId) {
|
|
@@ -642,11 +686,556 @@ async function listAllProjects(params) {
|
|
|
642
686
|
return all;
|
|
643
687
|
}
|
|
644
688
|
function repoLabel(p) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
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("");
|
|
650
1239
|
}
|
|
651
1240
|
var dbtSetupFlow = {
|
|
652
1241
|
initialState: () => ({}),
|
|
@@ -678,7 +1267,7 @@ var dbtSetupFlow = {
|
|
|
678
1267
|
throw new Error("dbt setup: incomplete state on finalize");
|
|
679
1268
|
}
|
|
680
1269
|
const allProjects = await listAllProjects(rt.params);
|
|
681
|
-
const
|
|
1270
|
+
const projectById = new Map(allProjects.map((p) => [String(p.id), p]));
|
|
682
1271
|
const targetIds = await resolveSetupSelection({
|
|
683
1272
|
selected: state.projects,
|
|
684
1273
|
allSentinel: ALL_PROJECTS,
|
|
@@ -689,25 +1278,97 @@ var dbtSetupFlow = {
|
|
|
689
1278
|
const accountId = rt.params[parameters.accountId.slug];
|
|
690
1279
|
const prodEnvId = rt.params[parameters.prodEnvId.slug];
|
|
691
1280
|
if (accountId) sections.push(`- Account: \`${accountId}\``);
|
|
692
|
-
if (prodEnvId) sections.push(`-
|
|
693
|
-
sections.push(
|
|
694
|
-
|
|
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) {
|
|
695
1288
|
sections.push("_No projects selected._", "");
|
|
696
1289
|
return sections.join("\n");
|
|
697
1290
|
}
|
|
698
|
-
sections.push("
|
|
699
|
-
sections.push("
|
|
700
|
-
|
|
701
|
-
|
|
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]);
|
|
702
1301
|
if (!p) {
|
|
703
|
-
sections.push(`| ${id} | - |`);
|
|
1302
|
+
sections.push(`| ${id} | - | ${runs} |`);
|
|
704
1303
|
continue;
|
|
705
1304
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1305
|
+
sections.push(
|
|
1306
|
+
`| ${escapePipe(p.name)} | ${escapePipe(repoLabel(p))} | ${runs} |`
|
|
1307
|
+
);
|
|
709
1308
|
}
|
|
710
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);
|
|
711
1372
|
return sections.join("\n");
|
|
712
1373
|
}
|
|
713
1374
|
};
|
|
@@ -812,7 +1473,7 @@ var dbtConnector = new ConnectorPlugin({
|
|
|
812
1473
|
systemPrompt: {
|
|
813
1474
|
en: `### Tools
|
|
814
1475
|
|
|
815
|
-
- \`
|
|
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.
|
|
816
1477
|
|
|
817
1478
|
### Business Logic
|
|
818
1479
|
|
|
@@ -921,7 +1582,7 @@ query($environmentId: BigInt!, $uniqueIds: [String!]!) {
|
|
|
921
1582
|
- Get \`ancestors { uniqueId name }\` or \`children { uniqueId name }\` within node`,
|
|
922
1583
|
ja: `### \u30C4\u30FC\u30EB
|
|
923
1584
|
|
|
924
|
-
- \`
|
|
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
|
|
925
1586
|
|
|
926
1587
|
### Business Logic
|
|
927
1588
|
|