Skip to content

Commit

Permalink
Merge branch 'main' into feat/licence-keys
Browse files Browse the repository at this point in the history
  • Loading branch information
zachaysan committed Nov 5, 2024
2 parents cfc3e28 + 865b26b commit 67adf66
Show file tree
Hide file tree
Showing 17 changed files with 228 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.150.0"
".": "2.151.0"
}
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [2.151.0](https://github.com/Flagsmith/flagsmith/compare/v2.150.0...v2.151.0) (2024-11-04)


### Features

* async the logic for cloning feature states into a cloned environment ([#4005](https://github.com/Flagsmith/flagsmith/issues/4005)) ([02f5f71](https://github.com/Flagsmith/flagsmith/commit/02f5f71f82bae1ec3536cb522fc0b684a2c27605))
* **ci:** add command to rollback migrations ([#4768](https://github.com/Flagsmith/flagsmith/issues/4768)) ([483cc87](https://github.com/Flagsmith/flagsmith/commit/483cc87fde03d2da465f9ec799bdbc746533f8d2))
* **export:** Add support for edge identities data ([#4654](https://github.com/Flagsmith/flagsmith/issues/4654)) ([f72c764](https://github.com/Flagsmith/flagsmith/commit/f72c764e59d44f3c50bafd0cd2aef2dcf51af07b))
* **permissions:** update endpoints to expose tag-supported perms ([#4788](https://github.com/Flagsmith/flagsmith/issues/4788)) ([43e68c1](https://github.com/Flagsmith/flagsmith/commit/43e68c1b67eeb5587440cbe5017035b60d897212))


### Bug Fixes

* Extend user first name length to 150 characters ([#4797](https://github.com/Flagsmith/flagsmith/issues/4797)) ([364c565](https://github.com/Flagsmith/flagsmith/commit/364c565fed5ebdb0da86927a25d56631502b3792))
* hide view features from associated segment overrides ([#4786](https://github.com/Flagsmith/flagsmith/issues/4786)) ([49ff569](https://github.com/Flagsmith/flagsmith/commit/49ff569cabac19f70c0688f1fe58c3511ce8801b))
* Set tag to get or create ([#4790](https://github.com/Flagsmith/flagsmith/issues/4790)) ([fedd296](https://github.com/Flagsmith/flagsmith/commit/fedd296a5cc8eb07aa1db4a2cbb5eca8f124c098))

## [2.150.0](https://github.com/Flagsmith/flagsmith/compare/v2.149.0...v2.150.0) (2024-10-30)


Expand Down
18 changes: 18 additions & 0 deletions api/users/migrations/0039_alter_ffadminuser_first_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.16 on 2024-11-04 17:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("users", "0038_create_hubspot_tracker"),
]

operations = [
migrations.AlterField(
model_name="ffadminuser",
name="first_name",
field=models.CharField(max_length=150, verbose_name="first name"),
),
]
2 changes: 1 addition & 1 deletion api/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class FFAdminUser(LifecycleModel, AbstractUser):
email = models.EmailField(unique=True, null=False)
objects = UserManager()
username = models.CharField(unique=True, max_length=150, null=True, blank=True)
first_name = models.CharField("first name", max_length=30)
first_name = models.CharField("first name", max_length=150)
last_name = models.CharField("last name", max_length=150)
google_user_id = models.CharField(max_length=50, null=True, blank=True)
github_user_id = models.CharField(max_length=50, null=True, blank=True)
Expand Down
12 changes: 12 additions & 0 deletions frontend/common/services/useFeatureVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from 'components/diff/diff-utils'
import { getSegments } from './useSegment'
import { getFeatureStates } from './useFeatureState'
import moment from 'moment'

const transformFeatureStates = (featureStates: TypedFeatureState[]) =>
featureStates?.map((v) => ({
Expand Down Expand Up @@ -322,6 +323,17 @@ export const {
// END OF EXPORTS
} = featureVersionService

export function isVersionOverLimit(
versionLimitDays: number | null | undefined,
date: string | undefined,
) {
if (!versionLimitDays) {
return false
}
const days = moment().diff(moment(date), 'days') + 1
return !!versionLimitDays && days > versionLimitDays
}

/* Usage examples:
const { data, isLoading } = useGetFeatureVersionQuery({ id: 2 }, {}) //get hook
const [createFeatureVersion, { isLoading, data, isSuccess }] = useCreateFeatureVersionMutation() //create hook
Expand Down
2 changes: 1 addition & 1 deletion frontend/common/services/useSubscriptionMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const getSubscriptionMetadataService = service
.injectEndpoints({
endpoints: (builder) => ({
getSubscriptionMetadata: builder.query<
Res['getSubscriptionMetadata'],
Res['subscriptionMetadata'],
Req['getSubscriptionMetadata']
>({
providesTags: (res) => [
Expand Down
9 changes: 4 additions & 5 deletions frontend/common/stores/organisation-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Constants from 'common/constants'
import { projectService } from 'common/services/useProject'
import { getStore } from 'common/store'
import sortBy from 'lodash/sortBy'
import { getSubscriptionMetadata } from 'common/services/useSubscriptionMetadata'

const Dispatcher = require('../dispatcher/dispatcher')
const BaseStore = require('./base/_store')
Expand Down Expand Up @@ -139,11 +140,9 @@ const controller = {
AccountStore.getOrganisationRole(id) === 'ADMIN'
? [
data.get(`${Project.api}organisations/${id}/invites/`),
data
.get(
`${Project.api}organisations/${id}/get-subscription-metadata/`,
)
.catch(() => null),
getSubscriptionMetadata(getStore(), { id }).then(
(res) => res.data,
),
]
: [],
),
Expand Down
4 changes: 3 additions & 1 deletion frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ export type InviteLink = {

export type SubscriptionMeta = {
max_seats: number | null
audit_log_visibility_days: number | null
feature_history_visibility_days: number | null
max_api_calls: number | null
max_projects: number | null
payment_source: string | null
Expand Down Expand Up @@ -700,7 +702,7 @@ export type Res = {
rolesPermissionUsers: PagedResponse<RolePermissionUser>
createRolePermissionGroup: RolePermissionGroup
rolePermissionGroup: PagedResponse<RolePermissionGroup>
getSubscriptionMetadata: { id: string; max_api_calls: number }
subscriptionMetadata: SubscriptionMeta
environment: Environment
metadataModelFieldList: PagedResponse<MetadataModelField>
metadataModelField: MetadataModelField
Expand Down
6 changes: 5 additions & 1 deletion frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export type PaidFeature =
| 'FORCE_2FA'
| '4_EYES'
| 'STALE_FLAGS'
| 'VERSIONING'
| 'VERSIONING_DAYS'
| 'AUDIT_DAYS'
| 'AUTO_SEATS'
| 'METADATA'
| 'REALTIME'
Expand Down Expand Up @@ -304,6 +305,9 @@ const Utils = Object.assign({}, require('./base/_utils'), {
},
getNextPlan: (skipFree?: boolean) => {
const currentPlan = Utils.getPlanName(AccountStore.getActiveOrgPlan())
if (currentPlan !== planNames.enterprise && !Utils.isSaas()) {
return planNames.enterprise
}
switch (currentPlan) {
case planNames.free: {
return skipFree ? planNames.startup : planNames.scaleUp
Expand Down
182 changes: 119 additions & 63 deletions frontend/web/components/AuditLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import PanelSearch from './PanelSearch'
import JSONReference from './JSONReference'
import moment from 'moment'
import PlanBasedBanner from './PlanBasedAccess'
import { useGetSubscriptionMetadataQuery } from 'common/services/useSubscriptionMetadata'
import AccountStore from 'common/stores/account-store'
import { isVersionOverLimit } from 'common/services/useFeatureVersion'
import Tooltip from './Tooltip'

type AuditLogType = {
environmentId: string
Expand All @@ -36,6 +40,9 @@ const AuditLog: FC<AuditLogType> = (props) => {
setPage(1)
},
)
const { data: subscriptionMeta } = useGetSubscriptionMetadataQuery({
id: AccountStore.getOrganisation()?.id,
})
const [environments, setEnvironments] = useState(props.environmentId)

useEffect(() => {
Expand Down Expand Up @@ -96,17 +103,49 @@ const AuditLog: FC<AuditLogType> = (props) => {
})
const colour = index === -1 ? 0 : index
let link: ReactNode = null
if (
related_object_uuid &&
related_object_type === 'EF_VERSION' &&
environment
) {
const date = moment(created_date)
const isVersionEvent =
related_object_uuid && related_object_type === 'EF_VERSION' && environment
const versionLimitDays = subscriptionMeta?.feature_history_visibility_days

const isOverLimit = isVersionEvent
? isVersionOverLimit(versionLimitDays, created_date)
: false
const VersionButton = (
<Button disabled={isOverLimit} theme='text'>
View version
</Button>
)

if (isVersionEvent) {
link = (
<Link
to={`/project/${project.id}/environment/${environment.api_key}/history/${related_object_uuid}/`}
<Tooltip
title={
<div className='d-flex gap-2'>
{isOverLimit ? (
VersionButton
) : (
<Link
to={`/project/${project.id}/environment/${environment.api_key}/history/${related_object_uuid}/`}
>
{VersionButton}
</Link>
)}
<PlanBasedBanner
force={isOverLimit}
feature={'VERSIONING_DAYS'}
theme={'badge'}
/>
</div>
}
>
<Button theme='text'>View version</Button>
</Link>
{isOverLimit
? `<div>
Unlock your feature's entire history.<br/>Currently limited to${' '}
<strong>${versionLimitDays} days</strong>.
</div>`
: ''}
</Tooltip>
)
}
const inner = (
Expand All @@ -115,7 +154,7 @@ const AuditLog: FC<AuditLogType> = (props) => {
className='table-column px-3 fs-small ln-sm'
style={{ width: widths[0] }}
>
{moment(created_date).format('Do MMM YYYY HH:mma')}
{date.format('Do MMM YYYY HH:mma')}
</div>
<div
className='table-column fs-small ln-sm'
Expand Down Expand Up @@ -164,63 +203,80 @@ const AuditLog: FC<AuditLogType> = (props) => {
}

const { env: envFilter } = Utils.fromParam()
const auditLimitDays = subscriptionMeta?.audit_log_visibility_days

return (
<PanelSearch
id='messages-list'
title='Log entries'
isLoading={isFetching}
className='no-pad'
items={projectAuditLog?.results}
filter={envFilter}
search={searchInput}
searchPanel={props.searchPanel}
onChange={(e: InputEvent) => {
setSearchInput(Utils.safeParseEventValue(e))
}}
paging={{
...(projectAuditLog || {}),
page,
pageSize: props.pageSize,
}}
nextPage={() => {
setPage(page + 1)
}}
prevPage={() => {
setPage(page - 1)
}}
goToPage={(page: number) => {
setPage(page)
}}
filterRow={() => true}
renderRow={renderRow}
header={
<Row className='table-header'>
<div className='table-column px-3' style={{ width: widths[0] }}>
Date
</div>
<div className='table-column' style={{ width: widths[1] }}>
User
</div>
<div className='table-column' style={{ width: widths[2] }}>
Environment
</div>
<Flex className='table-column'>Content</Flex>
</Row>
}
renderFooter={() => (
<JSONReference
className='mt-4 ml-2'
title={'Audit'}
json={projectAuditLog?.results}
<>
{!!auditLimitDays && (
<PlanBasedBanner
className='mb-4'
force
feature={'AUDIT_DAYS'}
title={
<div>
Unlock your audit log history. Currently limited to{' '}
<strong>{auditLimitDays} days</strong>.
</div>
}
theme={'description'}
/>
)}
renderNoResults={
<FormGroup className='text-center'>
You have no log messages for your project.
</FormGroup>
}
/>
<PanelSearch
id='messages-list'
title='Log entries'
isLoading={isFetching}
className='no-pad'
items={projectAuditLog?.results}
filter={envFilter}
search={searchInput}
searchPanel={props.searchPanel}
onChange={(e: InputEvent) => {
setSearchInput(Utils.safeParseEventValue(e))
}}
paging={{
...(projectAuditLog || {}),
page,
pageSize: props.pageSize,
}}
nextPage={() => {
setPage(page + 1)
}}
prevPage={() => {
setPage(page - 1)
}}
goToPage={(page: number) => {
setPage(page)
}}
filterRow={() => true}
renderRow={renderRow}
header={
<Row className='table-header'>
<div className='table-column px-3' style={{ width: widths[0] }}>
Date
</div>
<div className='table-column' style={{ width: widths[1] }}>
User
</div>
<div className='table-column' style={{ width: widths[2] }}>
Environment
</div>
<Flex className='table-column'>Content</Flex>
</Row>
}
renderFooter={() => (
<JSONReference
className='mt-4 ml-2'
title={'Audit'}
json={projectAuditLog?.results}
/>
)}
renderNoResults={
<FormGroup className='text-center'>
You have no log messages for your project.
</FormGroup>
}
/>
</>
)
}

Expand Down
Loading

0 comments on commit 67adf66

Please sign in to comment.