Skip to content

Commit 997ead8

Browse files
committed
feat(cc-addon-info): add Metabase
1 parent 067b4c3 commit 997ead8

File tree

5 files changed

+253
-4
lines changed

5 files changed

+253
-4
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { getAssetUrl } from '../../lib/assets-url.js';
2+
import { notifyError, notifySuccess } from '../../lib/notifications.js';
3+
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
4+
import { generateDocsHref } from '../../lib/utils.js';
5+
import { i18n } from '../../translations/translation.js';
6+
import '../cc-smart-container/cc-smart-container.js';
7+
import { CcAddonInfoClient, formatVersionState } from './cc-addon-info.client.js';
8+
import './cc-addon-info.js';
9+
10+
const PROVIDER_ID = 'metabase';
11+
12+
/**
13+
* @typedef {import('./cc-addon-info.js').CcAddonInfo} CcAddonInfo
14+
* @typedef {import('./cc-addon-info.types.js').AddonInfoStateLoaded} AddonInfoStateLoaded
15+
* @typedef {import('./cc-addon-info.types.js').AddonInfoStateLoading} AddonInfoStateLoading
16+
* @typedef {import('./cc-addon-info.types.js').AddonVersionStateUpdateAvailable} AddonVersionStateUpdateAvailable
17+
* @typedef {import('./cc-addon-info.types.js').AddonVersionStateRequestingUpdate} AddonVersionStateRequestingUpdate
18+
* @typedef {import('../../lib/smart/smart-component.types.js').OnContextUpdateArgs<CcAddonInfo>} OnContextUpdateArgs
19+
*/
20+
21+
defineSmartComponent({
22+
selector: 'cc-addon-info[smart-mode="metabase"]',
23+
params: {
24+
apiConfig: { type: Object },
25+
ownerId: { type: String },
26+
addonId: { type: String },
27+
appOverviewUrlPattern: { type: String },
28+
addonDashboardUrlPattern: { type: String },
29+
scalabilityUrlPattern: { type: String },
30+
grafanaLink: { type: Object, optional: true },
31+
logsUrlPattern: { type: String },
32+
},
33+
/** @param {OnContextUpdateArgs} _ */
34+
onContextUpdate({ context, onEvent, updateComponent, signal }) {
35+
const {
36+
apiConfig,
37+
ownerId,
38+
addonId,
39+
appOverviewUrlPattern,
40+
addonDashboardUrlPattern,
41+
scalabilityUrlPattern,
42+
grafanaLink,
43+
} = context;
44+
let logsUrl = '';
45+
46+
const api = new CcAddonInfoClient({ apiConfig, ownerId, addonId, providerId: PROVIDER_ID, grafanaLink, signal });
47+
48+
/** @type {AddonInfoStateLoading} */
49+
const LOADING_STATE = {
50+
type: 'loading',
51+
version: {
52+
stateType: 'up-to-date',
53+
installed: '0.0.0',
54+
latest: '0.0.0',
55+
},
56+
creationDate: '2025-08-06 15:03:00',
57+
// if Grafana is totally disabled within the console, do not display a skeleton for grafana link
58+
openGrafanaLink: grafanaLink != null ? 'https://example.com' : null,
59+
openScalabilityLink: '/placeholder',
60+
linkedServices: [
61+
{
62+
type: 'app',
63+
name: 'Java',
64+
logoUrl: null,
65+
link: 'https://example.com',
66+
},
67+
{
68+
type: 'addon',
69+
name: 'PostgreSQL',
70+
logoUrl: null,
71+
link: 'https://example.com',
72+
},
73+
{
74+
type: 'addon',
75+
name: 'FS Bucket',
76+
logoUrl: null,
77+
link: 'https://example.com',
78+
},
79+
],
80+
};
81+
82+
updateComponent('state', LOADING_STATE);
83+
updateComponent('docLink', {
84+
text: i18n('cc-addon-info.doc-link.metabase'),
85+
href: generateDocsHref('/addons/metabase'),
86+
});
87+
88+
api
89+
.getAddonInfo()
90+
.then(({ operatorVersionInfo, addonInfo, grafanaAppLink, operator }) => {
91+
const javaAppId = operator.resources.entrypoint;
92+
logsUrl = context.logsUrlPattern.replace(':id', javaAppId);
93+
94+
updateComponent('state', {
95+
type: 'loaded',
96+
version: formatVersionState(operatorVersionInfo),
97+
creationDate: addonInfo.creationDate,
98+
openGrafanaLink: grafanaAppLink,
99+
openScalabilityLink: scalabilityUrlPattern.replace(':id', javaAppId),
100+
linkedServices: [
101+
{
102+
type: 'app',
103+
name: 'Java',
104+
logoUrl: getAssetUrl('/logos/java-jar.svg'),
105+
link: appOverviewUrlPattern.replace(':id', javaAppId),
106+
},
107+
{
108+
type: 'addon',
109+
name: 'PostgreSQL',
110+
logoUrl: getAssetUrl('/logos/pgsql.svg'),
111+
link: addonDashboardUrlPattern.replace(':id', operator.resources.pgsqlId),
112+
},
113+
],
114+
});
115+
})
116+
.catch((error) => {
117+
console.error(error);
118+
updateComponent('state', { type: 'error' });
119+
});
120+
121+
onEvent('cc-addon-version-change', (targetVersion) => {
122+
updateComponent(
123+
'state',
124+
/** @param {AddonInfoStateLoaded & { version: AddonVersionStateUpdateAvailable | AddonVersionStateRequestingUpdate }} state */
125+
(state) => {
126+
state.version = {
127+
...state.version,
128+
stateType: 'requesting-update',
129+
};
130+
},
131+
);
132+
133+
api
134+
.updateOperatorVersion(targetVersion)
135+
.then(({ availableVersions }) => {
136+
notifySuccess(
137+
i18n('cc-addon-info.version.update.success.content', { logsUrl }),
138+
i18n('cc-addon-info.version.update.success.heading', { version: targetVersion }),
139+
);
140+
updateComponent(
141+
'state',
142+
/** @param {AddonInfoStateLoaded} state */
143+
(state) => {
144+
const needUpdate = targetVersion !== state.version.latest;
145+
state.version = formatVersionState({
146+
installed: targetVersion,
147+
available: availableVersions,
148+
latest: state.version.latest,
149+
needUpdate,
150+
});
151+
},
152+
);
153+
})
154+
.catch((error) => {
155+
updateComponent(
156+
'state',
157+
/** @param {AddonInfoStateLoaded & { version: AddonVersionStateUpdateAvailable | AddonVersionStateRequestingUpdate }} state */
158+
(state) => {
159+
state.version = {
160+
...state.version,
161+
stateType: 'update-available',
162+
};
163+
},
164+
);
165+
notifyError(i18n('cc-addon-info.version.update.error'));
166+
console.error(error);
167+
});
168+
});
169+
},
170+
});
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
kind: '🛠 Addon/<cc-addon-info>'
3+
title: '💡 Smart (Metabase)'
4+
---
5+
# 💡 Smart `<cc-addon-info smart-mode="metabase">`
6+
7+
## ℹ️ Details
8+
9+
<table>
10+
<tr><td><strong>Component </strong> <td><a href="🛠-addons-cc-addon-info--default-story"><code>cc-addon-info</code></a>
11+
<tr><td><strong>Selector </strong> <td><code>cc-addon-info[smart-mode="metabase"]</code>
12+
<tr><td><strong>Requires auth</strong> <td>Yes
13+
</table>
14+
15+
## ⚙️ Params
16+
17+
| Name | Type | Details | Default |
18+
| ----------------------------- | ------------- | ----------------------------------------------------------------------------------------------- | --------- |
19+
| `apiConfig` | `ApiConfig` | Object with API configuration (target host, tokens...) | |
20+
| `ownerId` | `string` | UUID prefixed with orga_ | |
21+
| `addonId` | `string` | ID of the add-on | |
22+
| `appOverviewUrlPattern` | `string` | Pattern for the application overview url | |
23+
| `addonDashboardUrlPattern` | `string` | Pattern for the addon dashboard url | |
24+
| `scalabilityUrlPattern` | `string` | Pattern for the scalability url | |
25+
| `grafanaLink` | `GrafanaLink` | Grafana configuration object (may be disabled in some environments) | Optional |
26+
| `logsUrlPattern` | `string` | Pattern for the logs url (Example : `/organisations/${ownerId}/applications/${appId}/logs`) | |
27+
28+
```ts
29+
interface ApiConfig {
30+
API_HOST: string;
31+
API_OAUTH_TOKEN: string;
32+
API_OAUTH_TOKEN_SECRET: string;
33+
OAUTH_CONSUMER_KEY: string;
34+
OAUTH_CONSUMER_SECRET: string;
35+
}
36+
37+
interface GrafanaLink {
38+
// Base used to build the URL leading to Grafana services, usually the Grafana host name
39+
base: string;
40+
// Console route leading to the Grafana org page where users may enable / disable Grafana
41+
console: string;
42+
}
43+
```
44+
45+
## 🌐 API endpoints
46+
47+
| Method | URL | Cache? |
48+
|----------|------------------------------------------------------------------------|---------|
49+
| `GET` | `/v2/organisations/${ownerId}/addons/${addonId}` | Default |
50+
| `GET` | `/v4/addon-providers/addon-metabase/addons/${realId}` | Default |
51+
| `GET` | `/v4/addon-providers/addon-metabase/addons/${realId}/version/check` | Default |
52+
| `GET` | `/v2/organisations/${ownerId}/grafana` | Default |
53+
| `POST` | `/v4/addon-providers/addon-metabase/addons/${realId}/version/update` | Default |
54+
55+
## ⬇️️ Examples
56+
57+
```html
58+
<cc-smart-container context='{
59+
"apiConfig": {
60+
"API_HOST": "",
61+
"API_OAUTH_TOKEN": "",
62+
"API_OAUTH_TOKEN_SECRET": "",
63+
"OAUTH_CONSUMER_KEY": "",
64+
"OAUTH_CONSUMER_SECRET": ""
65+
},
66+
"ownerId": "orga_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
67+
"addonId": "addon_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
68+
"appOverviewUrlPattern": "/organisations/orga_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/applications/:id",
69+
"addonDashboardUrlPattern": "/organisations/orga_3547a882-d464-4c34-8168-addons/:id",
70+
"scalabilityUrlPattern": "/organisations/orga_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/applications/:id/settings",
71+
"grafanaLink": {
72+
"base": "https://grafana.services.example.com",
73+
"console": "https://console.example.com/organisations/orga_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/grafana"
74+
},
75+
"logsUrlPattern": "/organisations/orga_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/applications/:id/logs"
76+
}'>
77+
<cc-addon-info smart-mode="metabase"></cc-addon-info>
78+
</cc-smart-container>
79+
```

src/components/cc-addon-info/cc-addon-info.stories.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ export const metabase = makeStory(conf, {
144144
...metabaseInfo,
145145
},
146146
innerHTML: `
147-
<p slot="billing">Discover Metabase at no extra cost. Services and management fees are added to the price of these resources. <strong>During the discovery phase, these fees are offered free of charge.</strong></p>
148-
<p slot="linked-services"><em>Metabase, easy to configure and hosted on our services, is now generally available. It deploys a Java application and a PostgreSQL add-on. You can scale them as you grow.</em></p>
147+
<p slot="billing">Operator services and management fees are added to the price of these resources.<br><strong>During the discovery phase, these fees are offered free of charge</strong>.</p>
149148
`,
150149
},
151150
{
@@ -155,8 +154,7 @@ export const metabase = makeStory(conf, {
155154
...metabaseInfo,
156155
},
157156
innerHTML: `
158-
<p slot="billing">Discover Metabase at no extra cost. Services and management fees are added to the price of these resources. <strong>During the discovery phase, these fees are offered free of charge.</strong></p>
159-
<p slot="linked-services"><em>Metabase, easy to configure and hosted on our services, is now generally available. It deploys a Java application and a PostgreSQL add-on. You can scale them as you grow.</em></p>
157+
<p slot="billing">Operator services and management fees are added to the price of these resources.<br><strong>During the discovery phase, these fees are offered free of charge</strong>.</p>
160158
`,
161159
},
162160
],

src/translations/translations.en.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ export const translations = {
298298
formatDatetime(date),
299299
'cc-addon-info.doc-link.keycloak': `Keycloak - Documentation`,
300300
'cc-addon-info.doc-link.matomo': `Matomo - Documentation`,
301+
'cc-addon-info.doc-link.metabase': `Metabase - Documentation`,
301302
'cc-addon-info.doc-link.otoroshi': `Otoroshi - Documentation`,
302303
'cc-addon-info.error': `Something went wrong while loading add-on information.`,
303304
'cc-addon-info.feature.connection-limit': `Connection limit`,

src/translations/translations.fr.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ export const translations = {
309309
formatDatetime(date),
310310
'cc-addon-info.doc-link.keycloak': `Keycloak - Documentation`,
311311
'cc-addon-info.doc-link.matomo': `Matomo - Documentation`,
312+
'cc-addon-info.doc-link.metabase': `Metabase - Documentation`,
312313
'cc-addon-info.doc-link.otoroshi': `Otoroshi - Documentation`,
313314
'cc-addon-info.error': `Une erreur est survenue pendant le chargement des informations de l'add-on.`,
314315
'cc-addon-info.feature.connection-limit': `Limite de connexions`,

0 commit comments

Comments
 (0)