diff --git a/frontend/api/index.js b/frontend/api/index.js
index 6302af8af8b6..c36666d4b2d8 100755
--- a/frontend/api/index.js
+++ b/frontend/api/index.js
@@ -49,6 +49,7 @@ app.get('/config/project-overrides', (req, res) => {
*/
const values = [
+ { name: 'isFlagsmithOnFlagsmith', value: !!process.env.FLAGSMITH_ON_FLAGSMITH_API_KEY && !!process.env.FLAGSMITH_ON_FLAGSMITH_API_URL },
{ name: 'preventSignup', value: envToBool('PREVENT_SIGNUP', false) },
{
name: 'preventEmailPassword',
diff --git a/frontend/common/stores/base/_store.js b/frontend/common/stores/base/_store.js
index 1f17ec97fff2..34c2e38d9ac5 100644
--- a/frontend/common/stores/base/_store.js
+++ b/frontend/common/stores/base/_store.js
@@ -18,6 +18,7 @@ module.exports = Object.assign({}, EventEmitter.prototype, {
// console.log('change', this.id)
this.trigger(DEFAULT_CHANGE_EVENT)
},
+ // Config store uses {type: FlagsmithStartupErrors, message: string}
error: null,
goneABitWest() {
this.hasLoaded = true
@@ -32,9 +33,9 @@ module.exports = Object.assign({}, EventEmitter.prototype, {
isSaving: false,
- loaded() {
+ loaded(persistError = false) {
this.hasLoaded = true
- this.error = null
+ this.error = persistError ? this.error : null
this.isLoading = false
this.trigger(DEFAULT_LOADED_EVENT)
this.trigger(DEFAULT_CHANGE_EVENT)
diff --git a/frontend/common/stores/config-store.js b/frontend/common/stores/config-store.js
index 38f79c4c319a..f0e7b4aa82da 100644
--- a/frontend/common/stores/config-store.js
+++ b/frontend/common/stores/config-store.js
@@ -22,8 +22,25 @@ const controller = {
}
store.model = flagsmith.getAllFlags()
},
- onError() {
- store.error = true
+ onError(e) {
+ if (
+ Project.isFlagsmithOnFlagsmith ||
+ (!Project.flagsmith && !Project.flagsmithClientAPI)
+ ) {
+ store.model = {}
+ // TODO: Migrate to TS and use enum
+ store.error = {
+ message: e?.message,
+ type: 'fof_init_error',
+ }
+ store.loaded(true)
+ return
+ }
+
+ store.error = {
+ message: e,
+ type: 'unknown',
+ }
store.goneABitWest()
},
}
@@ -54,8 +71,9 @@ flagsmith
onChange: controller.loaded,
realtime: Project.flagsmithRealtime,
})
- .catch(() => {
- controller.onError()
+ .catch((e) => {
+ console.error(e)
+ controller.onError(e)
})
controller.store = store
diff --git a/frontend/common/utils/utils.tsx b/frontend/common/utils/utils.tsx
index f0323e1c2ed3..359e2436393b 100644
--- a/frontend/common/utils/utils.tsx
+++ b/frontend/common/utils/utils.tsx
@@ -207,7 +207,8 @@ const Utils = Object.assign({}, require('./base/_utils'), {
* only add behaviour to Flagsmith-on-Flagsmith flags that have been explicitly created by customers.
*/
flagsmithFeatureExists(flag: string) {
- return Object.prototype.hasOwnProperty.call(flagsmith.getAllFlags(), flag)
+ const allFlags = flagsmith?.getAllFlags()
+ return allFlags && Object.prototype.hasOwnProperty.call(allFlags, flag)
},
getContentType(contentTypes: ContentType[], model: string, type: string) {
return contentTypes.find((c: ContentType) => c[model] === type) || null
@@ -403,6 +404,53 @@ const Utils = Object.assign({}, require('./base/_utils'), {
}
return true
},
+ getExistingWaitForTime: (
+ waitFor: string | undefined,
+ ): { amountOfTime: number; timeUnit: (typeof TimeUnit)[keyof typeof TimeUnit] } | undefined => {
+ if (!waitFor) {
+ return
+ }
+
+ const timeParts = waitFor.split(':')
+
+ if (timeParts.length != 3) return
+
+ const [hours, minutes, seconds] = timeParts
+
+ const amountOfMinutes = Number(minutes)
+ const amountOfHours = Number(hours)
+ const amountOfSeconds = Number(seconds)
+
+ if (amountOfHours + amountOfMinutes + amountOfSeconds === 0) {
+ return
+ }
+
+ // Days
+ if (
+ amountOfHours % 24 === 0 &&
+ amountOfMinutes === 0 &&
+ amountOfSeconds === 0
+ ) {
+ return {
+ amountOfTime: amountOfHours / 24,
+ timeUnit: TimeUnit.DAY,
+ }
+ }
+
+ // Hours
+ if (amountOfHours > 0 && amountOfMinutes === 0 && amountOfSeconds === 0) {
+ return {
+ amountOfTime: amountOfHours,
+ timeUnit: TimeUnit.HOUR,
+ }
+ }
+
+ // Minutes
+ return {
+ amountOfTime: amountOfMinutes,
+ timeUnit: TimeUnit.MINUTE,
+ }
+ },
getPlansPermission: (feature: PaidFeature) => {
const isOrgPermission = feature !== '2FA'
const plans = isOrgPermission
@@ -423,6 +471,7 @@ const Utils = Object.assign({}, require('./base/_utils'), {
getProjectColour(index: number) {
return Constants.projectColors[index % (Constants.projectColors.length - 1)]
},
+
getRequiredPlan: (feature: PaidFeature) => {
let plan
switch (feature) {
@@ -538,58 +587,10 @@ const Utils = Object.assign({}, require('./base/_utils'), {
return str
},
-
getViewIdentitiesPermission() {
return 'VIEW_IDENTITIES'
},
- getExistingWaitForTime: (
- waitFor: string | undefined,
- ): { amountOfTime: number; timeUnit: (typeof TimeUnit)[keyof typeof TimeUnit] } | undefined => {
- if (!waitFor) {
- return
- }
-
- const timeParts = waitFor.split(':')
-
- if (timeParts.length != 3) return
-
- const [hours, minutes, seconds] = timeParts
-
- const amountOfMinutes = Number(minutes)
- const amountOfHours = Number(hours)
- const amountOfSeconds = Number(seconds)
-
- if (amountOfHours + amountOfMinutes + amountOfSeconds === 0) {
- return
- }
- // Days
- if (
- amountOfHours % 24 === 0 &&
- amountOfMinutes === 0 &&
- amountOfSeconds === 0
- ) {
- return {
- amountOfTime: amountOfHours / 24,
- timeUnit: TimeUnit.DAY,
- }
- }
-
- // Hours
- if (amountOfHours > 0 && amountOfMinutes === 0 && amountOfSeconds === 0) {
- return {
- amountOfTime: amountOfHours,
- timeUnit: TimeUnit.HOUR,
- }
- }
-
- // Minutes
- return {
- amountOfTime: amountOfMinutes,
- timeUnit: TimeUnit.MINUTE,
- }
- },
-
hasEntityPermission(key: string, entityPermissions: UserPermissions) {
if (entityPermissions?.admin) return true
return !!entityPermissions?.permissions?.find(
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 516f1e5ad341..110f0ddd5f8c 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -9944,7 +9944,8 @@
"node_modules/flagsmith": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/flagsmith/-/flagsmith-9.0.0.tgz",
- "integrity": "sha512-hIipa+/ymTYtyEeDU181iEiZ6NbSXEpcYqJdRaY2A9zRhDh3sHNCqXvtWMxOuVOrBEt4HZZ/IsJ4eAsIV5me9A=="
+ "integrity": "sha512-hIipa+/ymTYtyEeDU181iEiZ6NbSXEpcYqJdRaY2A9zRhDh3sHNCqXvtWMxOuVOrBEt4HZZ/IsJ4eAsIV5me9A==",
+ "license": "BSD-3-Clause"
},
"node_modules/flat": {
"version": "5.0.2",
diff --git a/frontend/web/components/App.js b/frontend/web/components/App.js
index 89775baabaf7..8e6389b2a5bb 100644
--- a/frontend/web/components/App.js
+++ b/frontend/web/components/App.js
@@ -1,4 +1,4 @@
-import React, { Component, Fragment } from 'react'
+import React, { Component } from 'react'
import { matchPath, withRouter } from 'react-router-dom'
import * as amplitude from '@amplitude/analytics-browser'
import { plugin as engagementPlugin } from '@amplitude/engagement-browser'
@@ -16,6 +16,10 @@ import { resolveAuthFlow } from '@datadog/ui-extensions-sdk'
import ConfigProvider from 'common/providers/ConfigProvider'
import AccountStore from 'common/stores/account-store'
import OrganisationLimit from './OrganisationLimit'
+import {
+ getStartupErrorText,
+ isFlagsmithOnFlagsmithError,
+} from './base/errors/init.error'
import OrganisationStore from 'common/stores/organisation-store'
import ScrollToTop from './ScrollToTop'
import AnnouncementPerPage from './AnnouncementPerPage'
@@ -240,9 +244,32 @@ const App = class extends Component {
) {
return
The error was: "{fofError}"
++ Flag evaluation for is not affected, but some dashboard features might + be unavailable. +
++ Check with{' '} + + Flagsmith-on-Flagsmith documentation + {' '} + for more info. +
+
Sorry for the inconvenience, we will be back up and running shortly.
@@ -38,4 +25,6 @@ const HomePage = class extends React.Component {
)
}
-module.exports = ConfigProvider(HomePage)
+MaintenancePage.displayName = 'MaintenancePage'
+
+export default ConfigProvider(MaintenancePage)
diff --git a/frontend/web/components/base/errors/init.error.ts b/frontend/web/components/base/errors/init.error.ts
new file mode 100644
index 000000000000..6600651ebf89
--- /dev/null
+++ b/frontend/web/components/base/errors/init.error.ts
@@ -0,0 +1,37 @@
+export enum FlagsmithStartupErrors {
+ FOF_INIT_ERROR = 'fof_init_error',
+ UNKNOWN_ERROR = 'unknown_error',
+ MAINTENANCE_MODE = 'maintenance_mode',
+}
+
+export interface SDKInitErrors {
+ type: FlagsmithStartupErrors
+ message: string
+}
+
+const errorLabels: RecordFeatures ({features.length})
diff --git a/frontend/web/project/toast.tsx b/frontend/web/project/toast.tsx
index f2cd888f0f6c..81d631892d76 100644
--- a/frontend/web/project/toast.tsx
+++ b/frontend/web/project/toast.tsx
@@ -22,12 +22,14 @@ export interface MessageProps {
isRemoving?: boolean
theme?: ThemeType
children?: React.ReactNode
+ extraStyles?: { size?: 'large' | undefined }
}
const Message: FC