Skip to content
Snippets Groups Projects
Unverified Commit 73b6ee7d authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Merge MetabaseSettings and metabase-types settings type (#27528)

* Add missing fields to `Settings` type

* Make `MetabaseSettings` use up-to-date setting types

* Fix type errors
parent c7483334
No related branches found
No related tags found
No related merge requests found
......@@ -15,7 +15,7 @@ export interface FontWidgetProps {
const FontWidget = ({
setting,
settingValues,
availableFonts = MetabaseSettings.get("available-fonts"),
availableFonts = MetabaseSettings.get("available-fonts") || [],
onChange,
onChangeSetting,
}: FontWidgetProps): JSX.Element => {
......
......@@ -7,6 +7,8 @@ import {
Settings,
TokenFeatures,
Version,
VersionInfo,
VersionInfoRecord,
} from "metabase-types/api";
export const createMockEngine = (opts?: Partial<Engine>): Engine => ({
......@@ -64,6 +66,24 @@ export const createMockVersion = (opts?: Partial<Version>): Version => ({
...opts,
});
export const createMockVersionInfoRecord = (
opts?: Partial<VersionInfoRecord>,
): VersionInfoRecord => ({
version: "v1",
released: "2021-01-01",
patch: true,
highlights: ["Bug fix"],
...opts,
});
export const createMockVersionInfo = (
opts?: Partial<VersionInfo>,
): VersionInfo => ({
latest: createMockVersionInfoRecord(),
older: [createMockVersionInfoRecord()],
...opts,
});
export const createMockTokenStatus = () => ({
status: "Token is Valid.",
valid: true,
......@@ -108,19 +128,29 @@ export const createMockSettingDefinition = (
});
export const createMockSettings = (opts?: Partial<Settings>): Settings => ({
"admin-email": "admin@metabase.test",
"anon-tracking-enabled": false,
"application-font": "Lato",
"application-font-files": [],
"application-name": "Metabase",
"available-fonts": [],
"available-locales": null,
"cloud-gateway-ips": null,
"custom-formatting": {},
"deprecation-notice-version": undefined,
"email-configured?": false,
"enable-embedding": false,
"enable-enhancements?": false,
"enable-nested-queries": true,
"enable-query-caching": undefined,
"enable-password-login": true,
"enable-public-sharing": false,
"enable-xrays": false,
"experimental-enable-actions": false,
engines: createMockEngines(),
"has-user-setup": true,
"hide-embed-branding?": true,
"ga-enabled": false,
"google-auth-auto-create-accounts-domain": null,
"google-auth-client-id": null,
"google-auth-configured": false,
......@@ -131,11 +161,18 @@ export const createMockSettings = (opts?: Partial<Settings>): Settings => ({
"ldap-configured?": false,
"ldap-enabled": false,
"loading-message": "doing-science",
"other-sso-enabled?": null,
"password-complexity": { total: 6, digit: 1 },
"persisted-models-enabled": false,
"premium-embedding-token": null,
"report-timezone-short": "UTC",
"saml-configured": false,
"saml-enabled": false,
"snowplow-url": "",
"search-typeahead-enabled": true,
"setup-token": null,
"session-cookies": null,
"snowplow-enabled": false,
"show-database-syncing-modal": false,
"show-homepage-data": false,
"show-homepage-pin-message": false,
......@@ -144,13 +181,17 @@ export const createMockSettings = (opts?: Partial<Settings>): Settings => ({
"show-metabot": true,
"site-locale": "en",
"site-url": "http://localhost:3000",
"site-uuid": "1234",
"slack-app-token": null,
"slack-files-channel": null,
"slack-token": null,
"slack-token-valid?": false,
"subscription-allowed-domains": null,
"token-features": createMockTokenFeatures(),
"token-status": null,
engines: createMockEngines(),
"user-locale": null,
version: createMockVersion(),
"version-info": createMockVersionInfo(),
"version-info-last-checked": null,
...opts,
});
......@@ -100,7 +100,19 @@ export interface FontFile {
export type FontFormat = "woff" | "woff2" | "truetype";
export interface Version {
tag: string;
tag?: string;
}
export interface VersionInfoRecord {
version?: string; // tag
released?: string; // year-month-day
patch?: boolean;
highlights?: string[];
}
export interface VersionInfo {
latest?: VersionInfoRecord;
older?: VersionInfoRecord[];
}
export type LocaleData = [string, string];
......@@ -128,43 +140,65 @@ export interface TokenFeatures {
whitelabel: boolean;
}
export type PasswordComplexity = {
total?: number;
digit?: number;
};
export interface SettingDefinition {
key: string;
env_name?: string;
is_env_setting: boolean;
value: unknown;
value?: unknown;
}
export interface Settings {
"admin-email": string;
"anon-tracking-enabled": boolean;
"application-font": string;
"application-font-files": FontFile[] | null;
"application-name": string;
"available-fonts": string[];
"available-locales": LocaleData[] | null;
"cloud-gateway-ips": string[] | null;
"custom-formatting": FormattingSettings;
"deprecation-notice-version"?: string;
"email-configured?": boolean;
"embedding-secret-key"?: string;
"enable-embedding": boolean;
"enable-enhancements?": boolean;
"enable-nested-queries": boolean;
"enable-query-caching"?: boolean;
"enable-password-login": boolean;
"enable-public-sharing": boolean;
"enable-xrays": boolean;
engines: Record<string, Engine>;
"experimental-enable-actions": boolean;
"ga-enabled": boolean;
"google-auth-auto-create-accounts-domain": string | null;
"google-auth-client-id": string | null;
"google-auth-configured": boolean;
"google-auth-enabled": boolean;
"has-user-setup": boolean;
"hide-embed-branding?": boolean;
"is-hosted?": boolean;
"jwt-enabled"?: boolean;
"jwt-configured"?: boolean;
"ldap-configured?": boolean;
"ldap-enabled": boolean;
"loading-message": LoadingMessage;
"other-sso-enabled?": boolean | null;
"password-complexity": PasswordComplexity;
"persisted-models-enabled": boolean;
"premium-embedding-token": string | null;
"report-timezone-short": string;
"saml-configured"?: boolean;
"saml-enabled"?: boolean;
"search-typeahead-enabled": boolean;
"setup-token": string | null;
"session-cookies": boolean | null;
"snowplow-enabled": boolean;
"snowplow-url": string;
"show-database-syncing-modal": boolean;
"show-homepage-data": boolean;
"show-homepage-pin-message": boolean;
......@@ -172,15 +206,20 @@ export interface Settings {
"show-lighthouse-illustration": boolean;
"show-metabot": boolean;
"site-locale": string;
"site-uuid": string;
"site-url": string;
"slack-app-token": string | null;
"slack-files-channel": string | null;
"slack-token": string | null;
"slack-token-valid?": boolean;
"subscription-allowed-domains": string | null;
"token-features": TokenFeatures;
"token-status": TokenStatus | null;
engines: Record<string, Engine>;
"user-locale": string | null;
version: Version;
"version-info": VersionInfo | null;
"version-info-last-checked": string | null;
}
export type SettingKey = keyof Settings;
0;
......@@ -59,7 +59,7 @@ function ModelCachingControl({ database }: Props) {
? t`Turn model caching off`
: t`Turn model caching on`;
const siteUUID = MetabaseSettings.get("site-uuid");
const siteUUID = MetabaseSettings.get("site-uuid") || "";
const cacheSchemaName = getModelCacheSchemaName(databaseId, siteUUID);
const handleCachingChange = async () => {
......
......@@ -102,7 +102,10 @@ export function recipientIsValid(recipient: NotificationRecipient) {
const recipientDomain = MetabaseUtils.getEmailDomain(recipient.email);
const allowedDomains = MetabaseSettings.subscriptionAllowedDomains();
return _.isEmpty(allowedDomains) || allowedDomains.includes(recipientDomain);
return (
_.isEmpty(allowedDomains) ||
(recipientDomain && allowedDomains.includes(recipientDomain))
);
}
export function pulseIsValid(pulse: Pulse, channelSpecs: ChannelSpecs) {
......
import _ from "underscore";
import { t, ngettext, msgid } from "ttag";
import moment from "moment-timezone";
import { parseTimestamp } from "metabase/lib/time";
import MetabaseUtils from "metabase/lib/utils";
import { PasswordComplexity, SettingKey, Settings } from "metabase-types/api";
const n2w = (n: number) => MetabaseUtils.numberToWord(n);
const PASSWORD_COMPLEXITY_CLAUSES = {
......@@ -50,85 +53,21 @@ const PASSWORD_COMPLEXITY_CLAUSES = {
},
};
// TODO: dump this from backend settings definitions
export type SettingName =
| "application-name"
| "admin-email"
| "analytics-uuid"
| "anon-tracking-enabled"
| "site-locale"
| "user-locale"
| "available-locales"
| "available-timezones"
| "custom-formatting"
| "custom-geojson"
| "email-configured?"
| "enable-embedding"
| "enable-enhancements?"
| "enable-public-sharing"
| "enable-xrays"
| "experimental-enable-actions"
| "persisted-models-enabled"
| "engines"
| "ga-code"
| "ga-enabled"
| "google-auth-enabled"
| "google-auth-client-id"
| "has-sample-database?"
| "has-user-setup"
| "hide-embed-branding?"
| "is-hosted?"
| "ldap-enabled"
| "ldap-configured?"
| "other-sso-enabled?"
| "enable-password-login"
| "map-tile-server-url"
| "password-complexity"
| "persisted-model-refresh-interval-hours"
| "premium-features"
| "search-typeahead-enabled"
| "setup-token"
| "site-url"
| "site-uuid"
| "token-status"
| "types"
| "version-info-last-checked"
| "version-info"
| "version"
| "subscription-allowed-domains"
| "cloud-gateway-ips"
| "snowplow-enabled"
| "snowplow-url"
| "deprecation-notice-version"
| "show-database-syncing-modal"
| "premium-embedding-token"
| "metabase-store-managed"
| "application-colors"
| "application-font"
| "available-fonts"
| "enable-query-caching"
| "start-of-week"
| "report-timezone-short";
type SettingsMap = Record<SettingName, any>; // provides access to Metabase application settings
type SettingListener = (value: any) => void;
class Settings {
_settings: Partial<SettingsMap>;
_listeners: Partial<Record<SettingName, SettingListener[]>> = {};
class MetabaseSettings {
_settings: Partial<Settings>;
_listeners: Partial<{ [key: string]: SettingListener[] }> = {};
constructor(settings: Partial<SettingsMap> = {}) {
constructor(settings: Partial<Settings> = {}) {
this._settings = settings;
}
get(key: SettingName, defaultValue: any = null) {
return this._settings[key] !== undefined
? this._settings[key]
: defaultValue;
get<T extends SettingKey>(key: T): Partial<Settings>[T] {
return this._settings[key];
}
set(key: SettingName, value: any) {
set<T extends SettingKey>(key: T, value: Settings[T]) {
if (this._settings[key] !== value) {
this._settings[key] = value;
const listeners = this._listeners[key];
......@@ -143,15 +82,15 @@ class Settings {
}
}
setAll(settings: SettingsMap) {
const keys = Object.keys(settings) as SettingName[];
setAll(settings: Settings) {
const keys = Object.keys(settings) as SettingKey[];
keys.forEach(key => {
this.set(key, settings[key]);
});
}
on(key: SettingName, callback: SettingListener) {
on(key: SettingKey, callback: SettingListener) {
this._listeners[key] = this._listeners[key] || [];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this._listeners[key]!.push(callback);
......@@ -166,12 +105,12 @@ class Settings {
return this.get("enable-enhancements?");
}
isEmailConfigured() {
return this.get("email-configured?");
isEmailConfigured(): boolean {
return !!this.get("email-configured?");
}
isHosted(): boolean {
return this.get("is-hosted?");
return !!this.get("is-hosted?");
}
cloudGatewayIps(): string[] {
......@@ -267,7 +206,7 @@ class Settings {
}
docsUrl(page = "", anchor = "") {
let { tag } = this.get("version", {});
let { tag } = this.get("version") || {};
const matches = tag && tag.match(/v[01]\.(\d+)(?:\.\d+)?(-.*)?/);
if (matches) {
......@@ -330,27 +269,13 @@ class Settings {
return result != null && result >= 0;
}
/*
We expect the versionInfo to take on the JSON structure detailed below.
The 'older' section should contain only the last 5 previous versions, we don't need to go on forever.
The highlights for a version should just be text and should be limited to 5 items tops.
type VersionInfo = {
latest: Version,
older: Version[]
};
type Version = {
version: string, // e.x. "v0.17.1"
released: ISO8601Time,
patch: bool,
highlights: string[]
};
*/
versionInfo() {
return this.get("version-info", {});
return this.get("version-info") || {};
}
currentVersion() {
return this.get("version", {}).tag;
const version = this.get("version") || {};
return version.tag;
}
latestVersion() {
......@@ -366,9 +291,8 @@ class Settings {
return this.isHosted() || this.isEnterprise();
}
// returns a map that looks like {total: 6, digit: 1}
passwordComplexityRequirements() {
return this.get("password-complexity", {});
passwordComplexityRequirements(): PasswordComplexity {
return this.get("password-complexity") || {};
}
/**
......@@ -399,7 +323,7 @@ class Settings {
}
}
subscriptionAllowedDomains() {
subscriptionAllowedDomains(): string[] {
const setting = this.get("subscription-allowed-domains") || "";
return setting ? setting.split(",") : [];
}
......@@ -414,4 +338,4 @@ function makeRegexTest(property: string, regex: RegExp) {
const initValues =
typeof window !== "undefined" ? _.clone(window.MetabaseBootstrap) : null;
export default new Settings(initValues);
export default new MetabaseSettings(initValues);
import { t } from "ttag";
import moment, {
DurationInputArg1,
DurationInputArg2,
MomentInput,
} from "moment-timezone";
import moment, { DurationInputArg2, MomentInput } from "moment-timezone";
import MetabaseSettings from "metabase/lib/settings";
......@@ -110,7 +106,7 @@ export function getDefaultTimezone() {
export function getNumericDateStyleFromSettings() {
const dateStyle = getDateStyleFromSettings();
return /\//.test(dateStyle) ? dateStyle : "M/D/YYYY";
return dateStyle && /\//.test(dateStyle) ? dateStyle : "M/D/YYYY";
}
export function getRelativeTime(timestamp: string) {
......
......@@ -52,7 +52,7 @@ export const LOAD_LOCALE_DEFAULTS = "metabase/setup/LOAD_LOCALE_DEFAULTS";
export const loadLocaleDefaults = createThunkAction(
LOAD_LOCALE_DEFAULTS,
() => async (dispatch: any) => {
const data = MetabaseSettings.get("available-locales");
const data = MetabaseSettings.get("available-locales") || [];
const locale = getDefaultLocale(getLocales(data));
await dispatch(setLocale(locale));
},
......
......@@ -14,7 +14,7 @@ import {
const mapStateToProps = (state: State) => ({
locale: getLocale(state),
localeData: Settings.get("available-locales"),
localeData: Settings.get("available-locales") || [],
isStepActive: isStepActive(state, LANGUAGE_STEP),
isStepCompleted: isStepCompleted(state, LANGUAGE_STEP),
isSetupCompleted: isSetupCompleted(state),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment