1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

use internal::Private;

/// Prometheus label for [`ApplicationNameHint::Unspecified`].
const UNSPECIFIED_LABEL: &str = "unspecified";
/// Prometheus label for [`ApplicationNameHint::Unrecognized`].
const UNRECOGNIZED_LABEL: &str = "unrecognized";
/// Prometheus label for [`ApplicationNameHint::Psql`].
const PSQL_LABEL: &str = "psql";
/// Prometheus label for [`ApplicationNameHint::Dbt`].
const DBT_LABEL: &str = "dbt";
/// Prometheus label for [`ApplicationNameHint::WebConsole`].
const WEB_CONSOLE_LABEL: &str = "web_console";
/// Promehteus label for [`ApplicationNameHint::WebConsoleShell`].
const WEB_CONSOLE_SHELL_LABEL: &str = "web_console_shell";
/// Prometheus label for [`ApplicationNameHint::MzPsql`].
const MZ_PSQL_LABEL: &str = "mz_psql";
/// Prometheus label for [`ApplicationNameHint::MaterializeFivetranDestination`].
const MATERIALIZE_FIVETRAN_DESTINATION_LABEL: &str = "materialize_fivetran_destination";
/// Prometheus label for [`ApplicationNameHint::TerraformProviderMaterialize`].
const TERRAFORM_PROVIDER_MATERIALIZE_LABEL: &str = "terraform_provider_materialize";
/// Prometheus label for [`ApplicationNameHint::TablePlus`].
const TABLE_PLUS_LABEL: &str = "table_plus";
/// Prometheus label for [`ApplicationNameHint::DataGrip`].
const DATA_GRIP_LABEL: &str = "data_grip";
/// Prometheus label for [`ApplicationNameHint::DBeaver`].
const D_BEAVER_LABEL: &str = "dbeaver";
/// Prometheus label for [`ApplicationNameHint::MzVscode`].
const MZ_VSCODE_LABEL: &str = "mz_vscode";
/// Prometheus label for [`ApplicationNameHint::MzGrafanaIntegration`].
const MZ_GRAFANA_LABEL: &str = "mz_grafana";

/// A hint for what application is making a request to the adapter.
///
/// Note: [`ApplicationNameHint`] gets logged as a label in our Prometheus metrics, and for
/// labels we need to be conscious of the cardinality, so please be careful with how many
/// variants we add to this enum.
///
/// Note: each enum variant contains an `internal::Private` to prevent creating this enum
/// directly. To create an instance of [`ApplicationNameHint`] please see
/// [`ApplicationNameHint::from_str`].
#[derive(Debug, Copy, Clone)]
pub enum ApplicationNameHint {
    /// No `application_name` was set.
    Unspecified(Private),
    /// An `application_name` was set, but it's not one we recognize.
    Unrecognized(Private),
    /// Request came from `psql`.
    Psql(Private),
    /// Request came from `dbt`.
    Dbt(Private),
    /// Request came from our web console.
    WebConsole(Private),
    /// Request came from the SQL shell in our web console.
    WebConsoleShell(Private),
    /// Request came from the `psql` shell spawned by `mz`.
    MzPsql(Private),
    /// Request came from our Fivetran Destination,
    MaterializeFivetranDestination(Private),
    /// Request came from a version of our Terraform provider.
    TerraformProviderMaterialize(Private),
    /// Request came from TablePlus.
    TablePlus(Private),
    /// Request came from a version of DataGrip.
    DataGrip(Private),
    /// Request came from a version of DBeaver.
    DBeaver(Private),
    /// Request came from our Visual Studio Code integration.
    MzVscode(Private),
    /// Request came from our Grafana integration.
    MzGrafanaIntegration(Private),
}

impl ApplicationNameHint {
    pub fn from_str(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "psql" => ApplicationNameHint::Psql(Private),
            "dbt" => ApplicationNameHint::Dbt(Private),
            "web_console" => ApplicationNameHint::WebConsole(Private),
            "web_console_shell" => ApplicationNameHint::WebConsoleShell(Private),
            "mz_psql" => ApplicationNameHint::MzPsql(Private),
            // Note: Make sure this is kept in sync with the `fivetran-destination` crate.
            "materialize_fivetran_destination" => {
                ApplicationNameHint::MaterializeFivetranDestination(Private)
            }
            "tableplus" => ApplicationNameHint::TablePlus(Private),
            "mz_vscode" => ApplicationNameHint::MzVscode(Private),
            "mz_grafana_integration" => ApplicationNameHint::MzGrafanaIntegration(Private),
            // Terraform provides the version as a suffix.
            x if x.starts_with("terraform-provider-materialize") => {
                ApplicationNameHint::TerraformProviderMaterialize(Private)
            }
            // DataGrip provides the version as a suffix.
            x if x.starts_with("datagrip") => ApplicationNameHint::DataGrip(Private),
            // DBeaver provides the version as a suffix.
            x if x.starts_with("dbeaver") => ApplicationNameHint::DBeaver(Private),
            "" => ApplicationNameHint::Unspecified(Private),
            // TODO(parkertimmerman): We should keep some record of these "unrecognized"
            // names, and possibly support more popular ones in the future.
            _ => ApplicationNameHint::Unrecognized(Private),
        }
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            ApplicationNameHint::Unspecified(_) => UNSPECIFIED_LABEL,
            ApplicationNameHint::Unrecognized(_) => UNRECOGNIZED_LABEL,
            ApplicationNameHint::Psql(_) => PSQL_LABEL,
            ApplicationNameHint::Dbt(_) => DBT_LABEL,
            ApplicationNameHint::WebConsole(_) => WEB_CONSOLE_LABEL,
            ApplicationNameHint::WebConsoleShell(_) => WEB_CONSOLE_SHELL_LABEL,
            ApplicationNameHint::MzPsql(_) => MZ_PSQL_LABEL,
            ApplicationNameHint::MaterializeFivetranDestination(_) => {
                MATERIALIZE_FIVETRAN_DESTINATION_LABEL
            }
            ApplicationNameHint::TablePlus(_) => TABLE_PLUS_LABEL,
            ApplicationNameHint::MzVscode(_) => MZ_VSCODE_LABEL,
            ApplicationNameHint::MzGrafanaIntegration(_) => MZ_GRAFANA_LABEL,
            ApplicationNameHint::TerraformProviderMaterialize(_) => {
                TERRAFORM_PROVIDER_MATERIALIZE_LABEL
            }
            ApplicationNameHint::DataGrip(_) => DATA_GRIP_LABEL,
            ApplicationNameHint::DBeaver(_) => D_BEAVER_LABEL,
        }
    }

    /// Returns whether or not we should trace errors for this requests with this application name.
    pub fn should_trace_errors(&self) -> bool {
        // Note(parkmycar): For now we only trace errors for the web console since we contol all of
        // those queries and in general they should never fail. As opposed to user queries which
        // are arbitrary.
        matches!(self, ApplicationNameHint::WebConsole(_))
    }
}

mod internal {
    #[derive(Debug, Copy, Clone)]
    pub struct Private;
}