Repository: prometheus/prometheus (language: go)

Issue:
codemirror-promql: Custom prometheus clients are not being used
`codemirror-promql` uses the default prometheus client even after [overriding it](https://github.com/prometheus/prometheus/tree/main/web/ui/module/codemirror-promql#override-the-default-prometheus-client).

I've looked into the source and found that the [`isPrometheusConfig` function](https://github.com/prometheus/prometheus/blob/main/web/ui/module/codemirror-promql/src/complete/index.ts#L34-L45) is responsible for the issue. It doesn't do a good job of differentiating between `PrometheusConfig` and `PrometheusClient` objects. For instance:

```typescript
import { HTTPPrometheusClient } from '@prometheus-io/codemirror-promql/dist/esm/client/prometheus';
console.log(isPrometheusConfig(new HTTPPrometheusClient({})))
// Prints true instead of false
```

Basically, any PrometheusClient which has a field named `url`, `lookbackInterval`, `httpErrorHandler`, `fetchFn`, `cache`, `httpMethod` or `apiPrefix` with a value that isn't `undefined` will make the function return true, effectively ignoring the custom client and treating it as a PrometheusConfig.



Discussion / hints:
mmm that's annoying indeed. But I'm wondering how we can fix it. Or are we limited by typescript and the way to make the difference between two types ?

Fix patch:
diff --git a/web/ui/module/codemirror-promql/src/complete/index.ts b/web/ui/module/codemirror-promql/src/complete/index.ts
index a28b7583d62..b3902c3b6b0 100644
--- a/web/ui/module/codemirror-promql/src/complete/index.ts
+++ b/web/ui/module/codemirror-promql/src/complete/index.ts
@@ -31,16 +31,14 @@ export interface CompleteConfiguration {
   completeStrategy?: CompleteStrategy;
 }
 
-function isPrometheusConfig(remoteConfig: PrometheusConfig | PrometheusClient): remoteConfig is PrometheusConfig {
-  const cfg = remoteConfig as PrometheusConfig;
+export function isPrometheusClient(remoteConfig: PrometheusConfig | PrometheusClient): remoteConfig is PrometheusClient {
+  const client = remoteConfig as PrometheusClient;
   return (
-    cfg.url !== undefined ||
-    cfg.lookbackInterval !== undefined ||
-    cfg.httpErrorHandler !== undefined ||
-    cfg.fetchFn !== undefined ||
-    cfg.cache !== undefined ||
-    cfg.httpMethod !== undefined ||
-    cfg.apiPrefix !== undefined
+    typeof client.labelNames === 'function' &&
+    typeof client.labelValues === 'function' &&
+    typeof client.metricMetadata === 'function' &&
+    typeof client.series === 'function' &&
+    typeof client.metricNames === 'function'
   );
 }
 
@@ -49,7 +47,7 @@ export function newCompleteStrategy(conf?: CompleteConfiguration): CompleteStrat
     return conf.completeStrategy;
   }
   if (conf?.remote) {
-    if (!isPrometheusConfig(conf.remote)) {
+    if (isPrometheusClient(conf.remote)) {
       return new HybridComplete(conf.remote, conf.maxMetricsMetadata);
     }
     return new HybridComplete(new CachedPrometheusClient(new HTTPPrometheusClient(conf.remote), conf.remote.cache), conf.maxMetricsMetadata);


Test patch:
diff --git a/web/ui/module/codemirror-promql/src/complete/index.test.ts b/web/ui/module/codemirror-promql/src/complete/index.test.ts
new file mode 100644
index 00000000000..b2b9e32f931
--- /dev/null
+++ b/web/ui/module/codemirror-promql/src/complete/index.test.ts
@@ -0,0 +1,27 @@
+// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { isPrometheusClient } from './index';
+import { HTTPPrometheusClient } from '../client/prometheus';
+
+describe('type of remoteConfig', () => {
+  it('should be a prometheusClient', () => {
+    const client = new HTTPPrometheusClient({});
+    expect(isPrometheusClient(client)).toBe(true);
+  });
+
+  it('should be a remote config', () => {
+    const remote = { url: 'https://prometheus.io' };
+    expect(isPrometheusClient(remote)).toBe(false);
+  });
+});