Skip to content

Commit

Permalink
feat: Add metadata fields to core entities (FE) (#3212)
Browse files Browse the repository at this point in the history
Co-authored-by: Matthew Elwell <[email protected]>
  • Loading branch information
novakzaballa and matthewelwell authored May 15, 2024
1 parent e9246bc commit c5bd7a2
Show file tree
Hide file tree
Showing 35 changed files with 2,045 additions and 367 deletions.
12 changes: 12 additions & 0 deletions docs/docs/basic-features/managing-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,15 @@ other Environments within the Project.
### Multi-Variate Flag Use Cases

The primary use case for using Multi-Variate flags is to drive [A/B tests](/advanced-use/ab-testing.md).

### Use Metadata

When creating or updating a feature, you can add Metadata if you was created Metadata Fields in Project Settings ->
Metadata.

You can add the Metadata in the Feature Setting Tab.

If you have metadata for features, a list of fields that can be filled, saved, and will be stored with the feature's
save flag will be displayed.

![Image](/img/metadata/metadata-feature-1.png)
14 changes: 14 additions & 0 deletions docs/docs/basic-features/segments.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,17 @@ These are the default limits for segments and rules:
- 1000 bytes per segment rule value

See the [documentation on System Limits](system-administration/system-limits.md) for more details.

## Use Metadata

When creating or updating a feature, you can add Metadata if you was created Metadata Fields in Project Settings ->
Metadata.

You can add the Metadata value in the Feature Setting Tab.

When creating or updating a segment, you can add Metadata. You can add the Metadata value in the Settings Setting Tab.

If you have metadata for features, it will create a list of fields that can be filled, saved, and will be stored with
the feature's save flag.

![Image](/img/metadata/metadata-segment-1.png)
10 changes: 10 additions & 0 deletions docs/docs/system-administration/environment-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ This will result in identities receiving different variations and being evaluate
split operator) when evaluated against the remote API. Evaluations in local evaluation mode will not be affected.

:::

## Use Metadata

When creating or updating a segment, you can enhance its information by adding previously created and enabled metadata
fields. To create and enable this you can do it in the metadata tab on the project settings page.

If you have metadata for features, it will create a list of fields that can be filled, saved, and will be stored with
the feature's save flag.

![Image](/img/metadata/metadata-environment.png)
42 changes: 42 additions & 0 deletions docs/docs/system-administration/metadata/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: Metadata
sidebar_position: 110
---

Flagsmith allows certain Entities within a Project to have Metadata of different types.

## Core Entities that support Metadata

- **[Features](/basic-features/managing-features#use-metadata)**.
- **[Environment](/system-administration/environment-settings#use-metadata)**.
- **[Segments](/basic-features/segments#use-metadata)**.

## Metadata Fields

To be able to add Metadata to your Entities, you first need to create Metadata fields within Project Settings ->
Metadata.

Here you'll also need to define whether it's optional or required.

- **Optional**: You may or may not add Metadata to your Entities.
- **Required**: You won't be able to update or create an Entity within your Project unless you include this Metadata.

![Image](/img/metadata/metadata-fields.png)

### Types of Metadata Field

Metadata Field supports five primary types of metadata values, each serving distinct purposes:

**String**: A basic data type representing text or alphanumeric characters. Strings are versatile and can describe a
wide range of attributes or characteristics.

**URL**: A type specifically designed to store web addresses or Uniform Resource Locators.

**Integer**: A numeric data type representing whole numbers without decimal points. Integers are useful for quantifiable
properties or attributes.

**Multiline String**: Similar to a standard string but capable of storing multiline text. Multiline strings are
beneficial for longer descriptions or content blocks.

**Boolean**: A data type with only two possible values: true or false. Booleans are ideal for representing binary
attributes or conditions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/img/metadata/metadata-feature-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/img/metadata/metadata-fields.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/img/metadata/metadata-segment-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ export default {
'Set different values for your feature based on what segments users are in. Identity overrides will take priority over any segment override.',
TAGS_DESCRIPTION:
'Organise your flags with tags, tagging your features as "<strong>protected</strong>" will prevent them from accidentally being deleted.',
TOOLTIP_METADATA_DESCRIPTION: 'Add metadata in your',
USER_PROPERTY_DESCRIPTION:
'The name of the user trait or custom property belonging to the user, e.g. firstName',
WEBHOOKS_DESCRIPTION:
Expand Down
26 changes: 26 additions & 0 deletions frontend/common/services/useEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ export const environmentService = service
url: `environments/?project=${data.projectId}`,
}),
}),
updateEnvironment: builder.mutation<
Res['environment'],
Req['updateEnvironment']
>({
invalidatesTags: (res) => [
{ id: 'LIST', type: 'Environment' },
{ id: res?.id, type: 'Environment' },
],
query: (query: Req['updateEnvironment']) => ({
body: query.body,
method: 'PUT',
url: `environments/${query.id}/`,
}),
}),
// END OF ENDPOINTS
}),
})
Expand All @@ -47,11 +61,23 @@ export async function getEnvironment(
environmentService.endpoints.getEnvironment.initiate(data, options),
)
}
export async function updateEnvironment(
store: any,
data: Req['updateEnvironment'],
options?: Parameters<
typeof environmentService.endpoints.updateEnvironment.initiate
>[1],
) {
return store.dispatch(
environmentService.endpoints.updateEnvironment.initiate(data, options),
)
}
// END OF FUNCTION_EXPORTS

export const {
useGetEnvironmentQuery,
useGetEnvironmentsQuery,
useUpdateEnvironmentMutation,
// END OF EXPORTS
} = environmentService

Expand Down
137 changes: 137 additions & 0 deletions frontend/common/services/useMetadataField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'
import Utils from 'common/utils/utils'

export const metadataService = service
.enhanceEndpoints({ addTagTypes: ['Metadata'] })
.injectEndpoints({
endpoints: (builder) => ({
createMetadataField: builder.mutation<
Res['metadataField'],
Req['createMetadataField']
>({
invalidatesTags: [{ id: 'LIST', type: 'Metadata' }],
query: (query: Req['createMetadataField']) => ({
body: query.body,
method: 'POST',
url: `metadata/fields/`,
}),
}),
deleteMetadataField: builder.mutation<
Res['metadataField'],
Req['deleteMetadataField']
>({
invalidatesTags: [{ id: 'LIST', type: 'Metadata' }],
query: (query: Req['deleteMetadataField']) => ({
method: 'DELETE',
url: `metadata/fields/${query.id}/`,
}),
}),
getMetadataField: builder.query<
Res['metadataField'],
Req['getMetadataField']
>({
providesTags: (res) => [{ id: res?.id, type: 'Metadata' }],
query: (query: Req['getMetadataField']) => ({
url: `metadata/fields/${query.organisation_id}/`,
}),
}),
getMetadataFieldList: builder.query<
Res['metadataList'],
Req['getMetadataList']
>({
providesTags: [{ id: 'LIST', type: 'Metadata' }],
query: (query: Req['getMetadataList']) => ({
url: `metadata/fields/?${Utils.toParam(query)}`,
}),
}),
updateMetadataField: builder.mutation<
Res['metadataField'],
Req['updateMetadataField']
>({
invalidatesTags: (res) => [
{ id: 'LIST', type: 'Metadata' },
{ id: res?.id, type: 'Metadata' },
],
query: (query: Req['updateMetadataField']) => ({
body: query.body,
method: 'PUT',
url: `metadata/fields/${query.id}/`,
}),
}),
// END OF ENDPOINTS
}),
})

export async function createMetadataField(
store: any,
data: Req['createMetadataField'],
options?: Parameters<
typeof metadataService.endpoints.createMetadataField.initiate
>[1],
) {
return store.dispatch(
metadataService.endpoints.createMetadataField.initiate(data, options),
)
}
export async function deleteMetadataField(
store: any,
data: Req['deleteMetadataField'],
options?: Parameters<
typeof metadataService.endpoints.deleteMetadataField.initiate
>[1],
) {
return store.dispatch(
metadataService.endpoints.deleteMetadataField.initiate(data, options),
)
}
export async function getMetadata(
store: any,
data: Req['getMetadataField'],
options?: Parameters<
typeof metadataService.endpoints.getMetadataField.initiate
>[1],
) {
return store.dispatch(
metadataService.endpoints.getMetadataField.initiate(data, options),
)
}
export async function getMetadataList(
store: any,
data: Req['getMetadataList'],
options?: Parameters<
typeof metadataService.endpoints.getMetadataFieldList.initiate
>[1],
) {
return store.dispatch(
metadataService.endpoints.getMetadataFieldList.initiate(data, options),
)
}
export async function updateMetadata(
store: any,
data: Req['updateMetadataField'],
options?: Parameters<
typeof metadataService.endpoints.updateMetadataField.initiate
>[1],
) {
return store.dispatch(
metadataService.endpoints.updateMetadataField.initiate(data, options),
)
}
// END OF FUNCTION_EXPORTS

export const {
useCreateMetadataFieldMutation,
useDeleteMetadataFieldMutation,
useGetMetadataFieldListQuery,
useGetMetadataFieldQuery,
useUpdateMetadataFieldMutation,
// END OF EXPORTS
} = metadataService

/* Usage examples:
const { data, isLoading } = useGetMetadataFieldQuery({ id: 2 }, {}) //get hook
const [createMetadataField, { isLoading, data, isSuccess }] = useCreateMetadataFieldMutation() //create hook
metadataService.endpoints.getMetadataField.select({id: 2})(store.getState()) //access data from any function
*/
Loading

0 comments on commit c5bd7a2

Please sign in to comment.