-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: display warning and prevent creation on limit #2526
Changes from 46 commits
1398479
d92fa99
273a07b
03c8642
cbc4d49
d00a307
244f2c4
420a7f4
33004fa
71eecfa
3bd32ac
c4b4397
c5522c2
b4b69cf
480733e
5cd3658
6188849
a9eaa10
68c2881
417d301
96511af
9a2ec99
72ab33e
fef7879
5f6cfb3
01695e4
772d6da
46a5fa3
264290d
95f36f5
e8d9b3f
d656954
210b0d7
c6836cd
5b6f3bb
b8c5a18
6270848
323e153
e8f463d
5171523
3b7c45f
98fbeea
3a6f086
75558d4
62d2a4b
6d30b9d
16b7f8b
6586b7d
c9c2ace
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ | |
*.json | ||
*.handlebars | ||
*.css | ||
.bablerc | ||
.bablerc | ||
**/CHANGELOG.md |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Res } from 'common/types/responses' | ||
import { Req } from 'common/types/requests' | ||
import { service } from 'common/service' | ||
|
||
export const getSubscriptionMetadataService = service | ||
.enhanceEndpoints({ addTagTypes: ['GetSubscriptionMetadata'] }) | ||
.injectEndpoints({ | ||
endpoints: (builder) => ({ | ||
getSubscriptionMetadata: builder.query< | ||
Res['getSubscriptionMetadata'], | ||
Req['getSubscriptionMetadata'] | ||
>({ | ||
providesTags: (res) => [ | ||
{ id: res?.id, type: 'GetSubscriptionMetadata' }, | ||
], | ||
query: (query: Req['getSubscriptionMetadata']) => ({ | ||
url: `organisations/${query.id}/get-subscription-metadata/`, | ||
}), | ||
}), | ||
// END OF ENDPOINTS | ||
}), | ||
}) | ||
|
||
export async function getSubscriptionMetadata( | ||
store: any, | ||
data: Req['getSubscriptionMetadata'], | ||
options?: Parameters< | ||
typeof getSubscriptionMetadataService.endpoints.getSubscriptionMetadata.initiate | ||
>[1], | ||
) { | ||
return store.dispatch( | ||
getSubscriptionMetadataService.endpoints.getSubscriptionMetadata.initiate( | ||
data, | ||
options, | ||
), | ||
) | ||
} | ||
// END OF FUNCTION_EXPORTS | ||
|
||
export const { | ||
useGetSubscriptionMetadataQuery, | ||
// END OF EXPORTS | ||
} = getSubscriptionMetadataService | ||
|
||
/* Usage examples: | ||
const { data, isLoading } = useGetSubscriptionMetadataQuery({ id: 2 }, {}) //get hook | ||
const [getSubscriptionMetadata, { isLoading, data, isSuccess }] = useGetSubscriptionMetadataMutation() //create hook | ||
getSubscriptionMetadataService.endpoints.getSubscriptionMetadata.select({id: 2})(store.getState()) //access data from any function | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { getIsWidget } from 'components/pages/WidgetPage' | ||
|
||
import Constants from 'common/constants' | ||
import Utils from 'common/utils/utils' | ||
|
||
const Dispatcher = require('../dispatcher/dispatcher') | ||
const BaseStore = require('./base/_store') | ||
|
@@ -83,6 +84,14 @@ const controller = { | |
store.saved() | ||
}) | ||
}, | ||
getEnv: (envId) => { | ||
data.get(`${Project.api}environments/${envId}/`).then((environment) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used anymore? |
||
environment.total_segment_overrides = | ||
environment.total_segment_overrides || 0 | ||
store.model = Object.assign(store.model, { environment }) | ||
store.saved() | ||
}) | ||
}, | ||
getProject: (id, cb, force) => { | ||
if (force) { | ||
store.loading() | ||
|
@@ -92,6 +101,12 @@ const controller = { | |
data.get(`${Project.api}environments/?project=${id}`).catch(() => []), | ||
]) | ||
.then(([project, environments]) => { | ||
project.max_segments_allowed = project.max_segments_allowed | ||
project.max_features_allowed = project.max_features_allowed | ||
project.max_segment_overrides_allowed = | ||
project.max_segment_overrides_allowed | ||
project.total_features = project.total_features || 0 | ||
project.total_segments = project.total_segments || 0 | ||
store.model = Object.assign(project, { | ||
environments: _.sortBy(environments.results, 'name'), | ||
}) | ||
|
@@ -118,6 +133,12 @@ const controller = { | |
data.get(`${Project.api}environments/?project=${id}`).catch(() => []), | ||
]) | ||
.then(([project, environments]) => { | ||
project.max_segments_allowed = project.max_segments_allowed | ||
project.max_features_allowed = project.max_features_allowed | ||
project.max_segment_overrides_allowed = | ||
project.max_segment_overrides_allowed | ||
project.total_features = project.total_features || 0 | ||
project.total_segments = project.total_segments || 0 | ||
store.model = Object.assign(project, { | ||
environments: _.sortBy(environments.results, 'name'), | ||
}) | ||
|
@@ -162,6 +183,24 @@ const store = Object.assign({}, BaseStore, { | |
}) | ||
}, | ||
getEnvs: () => store.model && store.model.environments, | ||
getMaxFeaturesAllowed: () => { | ||
return store.model && store.model.max_features_allowed | ||
matthewelwell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
getMaxSegmentOverridesAllowed: () => { | ||
novakzaballa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return store.model && store.model.max_segment_overrides_allowed | ||
}, | ||
getMaxSegmentsAllowed: () => { | ||
return store.model && store.model.max_segments_allowed | ||
}, | ||
getTotalFeatures: () => { | ||
return store.model && store.model.total_features | ||
}, | ||
getTotalSegmentOverrides: () => { | ||
return store.model && store.model.environment.total_segment_overrides | ||
}, | ||
getTotalSegments: () => { | ||
return store.model && store.model.total_segments | ||
}, | ||
id: 'project', | ||
model: null, | ||
}) | ||
|
@@ -187,6 +226,9 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => { | |
case Actions.EDIT_ENVIRONMENT: | ||
controller.editEnv(action.env) | ||
break | ||
case Actions.GET_ENVIRONMENT: | ||
controller.getEnv(action.environmentId) | ||
break | ||
case Actions.DELETE_ENVIRONMENT: | ||
controller.deleteEnv(action.env) | ||
break | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,10 +19,14 @@ import { resolveAuthFlow } from '@datadog/ui-extensions-sdk' | |
import ConfigProvider from 'common/providers/ConfigProvider' | ||
import Permission from 'common/providers/Permission' | ||
import { getOrganisationUsage } from 'common/services/useOrganisationUsage' | ||
import { getSubscriptionMetadata } from 'common/services/useSubscriptionMetadata' | ||
import Button from './base/forms/Button' | ||
import Icon from 'components/Icon' | ||
import Icon from './Icon' | ||
import AccountStore from 'common/stores/account-store' | ||
import InfoMessage from './InfoMessage' | ||
import Format from 'common/utils/format' | ||
import ErrorMessage from 'components/ErrorMessage' | ||
import WarningMessage from 'components/WarningMessage' | ||
|
||
const App = class extends Component { | ||
static propTypes = { | ||
|
@@ -38,6 +42,7 @@ const App = class extends Component { | |
asideIsVisible: !isMobile, | ||
lastEnvironmentId: '', | ||
lastProjectId: '', | ||
maxApiCalls: 50000, | ||
pin: '', | ||
showAnnouncement: true, | ||
totalApiCalls: 0, | ||
|
@@ -70,6 +75,13 @@ const App = class extends Component { | |
AccountStore.getOrganisation()?.id && | ||
this.state.activeOrganisation !== AccountStore.getOrganisation().id | ||
) { | ||
getSubscriptionMetadata(getStore(), { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used anymore |
||
id: AccountStore.getOrganisation()?.id, | ||
}).then((res) => { | ||
this.setState({ | ||
maxApiCalls: res?.data?.max_api_calls, | ||
}) | ||
}) | ||
getOrganisationUsage(getStore(), { | ||
organisationId: AccountStore.getOrganisation()?.id, | ||
}).then((res) => { | ||
|
@@ -288,6 +300,17 @@ const App = class extends Component { | |
) | ||
const dismissed = flagsmith.getTrait('dismissed_announcement') | ||
const showBanner = !dismissed || dismissed !== announcementValue.id | ||
const maxApiCallsPercentage = Utils.calculateRemainingLimitsPercentage( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used anymore |
||
this.state.totalApiCalls, | ||
this.state.maxApiCalls, | ||
70, | ||
).percentage | ||
|
||
const alertMaxApiCallsText = `You have used ${Format.shortenNumber( | ||
this.state.totalApiCalls, | ||
)}/${Format.shortenNumber( | ||
this.state.maxApiCalls, | ||
)} of your allowed requests.` | ||
|
||
return ( | ||
<Provider store={getStore()}> | ||
|
@@ -484,6 +507,31 @@ const App = class extends Component { | |
</div> | ||
) : ( | ||
<Fragment> | ||
<Row> | ||
{user && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned on slack, I think this should be componentised so that you can use hooks to get the data. |
||
Utils.getFlagsmithHasFeature( | ||
'payments_enabled', | ||
) && | ||
Utils.getFlagsmithHasFeature( | ||
'max_api_calls_alert', | ||
) && | ||
(maxApiCallsPercentage && | ||
maxApiCallsPercentage < 100 ? ( | ||
<WarningMessage | ||
warningMessage={alertMaxApiCallsText} | ||
warningMessageClass={'announcement'} | ||
enabledButton | ||
/> | ||
) : ( | ||
maxApiCallsPercentage >= 100 && ( | ||
<ErrorMessage | ||
error={alertMaxApiCallsText} | ||
errorMessageClass={'announcement'} | ||
enabledButton | ||
/> | ||
) | ||
))} | ||
</Row> | ||
{user && | ||
showBanner && | ||
Utils.getFlagsmithHasFeature('announcement') && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ const Aside = class extends Component { | |
? {} | ||
: { live_from_after: new Date().toISOString() }, | ||
) | ||
AppActions.getEnv(this.props.environmentId) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to do this |
||
} | ||
}) | ||
} | ||
|
@@ -77,6 +78,7 @@ const Aside = class extends Component { | |
? {} | ||
: { live_from_after: new Date().toISOString() }, | ||
) | ||
AppActions.getEnv(this.props.environmentId) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this, we're already getting the project's environments in AppActions.getProject There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is triggered every time we change environments. It is done this way because we need to save the response to the store so we can use its data to determine the content of the alert and whether it will be displayed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use the environment endpoint because the project one doesn't have the limits information we need, and Matt asked to keep it this way since it will be more efficient in terms of querying the DB There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After our conversation, I think this can now be removed. |
||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why we need to get the environment this way? It adds to tech debt, ideally it would be an action in useEnvironment.ts