mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* Update system_user_detail to enable editing of User Attributes - This adds a `updateUserCustomProfileAttributesValues` method to client4 to allow updating attributes for a given userID, as a match to getUserCustomProfilesAttributesValues - Fixed issue in saveCustomProfileAttributes where it used the version that did not take the passed in userId * Fixes for tests * Update saveCustomProfileAttribute mock in users.test.ts The `saveCustomProfileAttribute` test was still using the old getCustomProfileAttributeValuesRoute instead of the same route that `getCustomProfileAttributeValues` uses. * Adding e2e tests for editing User attributes (custom and system) in System Console * Fix testing issue with selector for 'Authentication Method' * workflows/server-ci-report.yml: security fixes by validating inputs (#33892) * [MM-61899] Properly restrict users who previously shared a team from DMs/GMs when they no longer share a team. (#30094) * [MM-61899] Properly restrict users who previously shared a team from DMs/GMs when they no longer share a team. * Fix checks * Fix test * Fix i18n * Added E2E tests * Merge'd * Add restricted DM check to more places * Merge'd * Restrict patching the channel (updating the channel) * Update verbiage in the admin console * Fix lint * More tests --------- Co-authored-by: Mattermost Build <[email protected]> * Mirror Postgres 14 docker image (#34009) * ugprade to go 1.24.6 (#34004) * Bump Postgres minimum supported version to 14 (#34010) * Downgrade French language (#33826) * Downgrade French language * Update i18n.jsx * Updates buildFieldAttrs to preseve existing attrs when editing a field (#33991) * Updates buildFieldAttrs to preseve existing attrs when editing a field * Fix preserve option issue for select/multiselect type fields * Fix linter --------- Co-authored-by: Miguel de la Cruz <[email protected]> * MM-64423 - Removing the clear func on switch (#31214) * MM-64423 - Removing the clear func on switch * Select all teams by default * Updating test * Updating test * Removing showFilterHaveBeenReset * Removing search string * Do not clear the search term * Updating tests --------- Co-authored-by: Mattermost Build <[email protected]> * Mm 65123 remove channel abac ff (#33953) * MM-65123 - remove channel abac feature flag * enable the channel scope access control to true * fix linters * adjust expected error in tests * remove no longer needed comment * Remove write_restrictable from core ABAC settings and fix channel access control logic --------- Co-authored-by: Mattermost Build <[email protected]> * [MM-65837], [MM-65824] - Update Dependencies (#33972) * Update github.com/mholt/archives * Update github.com/spf13/viper * make batch migration worker tests less flaky --------- Co-authored-by: Jesse Hallam <[email protected]> * (a11y-test): team menu (#33998) * MM-66071: Do not error on empty slice in /groups/names (#34021) * Do not error on empty slice in /groups/names If group_names is an empty slice, this should not be an invalid parameter. We return what we were asked for: an empty array. * Avoid requests to /groups/names if list is empty If the list of group names is empty, we do not need to ask the server for the corresponding groups: we already know it'll be an empty list. * Fix ABAC not available for entry (#34027) Automatic Merge * Explicitly name Postgres container volume (#33954) * Explicitly name Postgres container volume * Remove unused server/docker-compose.yaml This file doesn't seem to actually be used. When we run docker compose locally, it uses docker-compose.makefile.yml merged with the output of build/docker-compose-generator/main.go. * Revert "Remove unused server/docker-compose.yaml" This reverts commit 5a4596521752585eba7153787abb5cb65d88dc33. * Update volume name * Flag post API (#33765) * Added enable/disable setting and feature flag * added rest of notifgication settings * Added backend for content flagging setting and populated notification values from server side defaults * WIP user selector * Added common reviewers UI * Added additonal reviewers section * WIP * WIP * Team table base * Added search in teams * Added search in teams * Added additional settings section * WIP * Inbtegrated reviewers settings * WIP * WIP * Added server side validation * cleanup * cleanup * [skip ci] * Some refactoring * type fixes * lint fix * test: add content flagging settings test file * test: add comprehensive unit tests for content flagging settings * enhanced tests * test: add test file for content flagging additional settings * test: add comprehensive unit tests for ContentFlaggingAdditionalSettingsSection * Added additoonal settings test * test: add empty test file for team reviewers section * test: add comprehensive unit tests for TeamReviewersSection component * test: update tests to handle async data fetching in team reviewers section * test: add empty test file for content reviewers component * feat: add comprehensive unit tests for ContentFlaggingContentReviewers component * Added ContentFlaggingContentReviewersContentFlaggingContentReviewers test * test: add notification settings test file for content flagging * test: add comprehensive unit tests for content flagging notification settings * Added ContentFlaggingNotificationSettingsSection tests * test: add user profile pill test file * test: add comprehensive unit tests for UserProfilePill component * refactor: Replace enzyme shallow with renderWithContext in user_profile_pill tests * Added UserProfilePill tests * test: add empty test file for content reviewers team option * test: add comprehensive unit tests for TeamOptionComponent * Added TeamOptionComponent tests * test: add empty test file for reason_option component * test: add comprehensive unit tests for ReasonOption component * Added ReasonOption tests * cleanup * Fixed i18n error * fixed e2e test lijnt issues * Updated test cases * Added snaoshot * Updated snaoshot * lint fix * WIP * lint fix * Added post flagging properties setup * review fixes * updated snapshot * CI * Added base APIs * Fetched team status data on load and team switch * WIP * Review fixes * wip * WIP * Removed an test, updated comment * CI * Added tests * Added tests * Lint fix * Added API specs * Fixed types * CI fixes * API tests * lint fixes * Set env variable so API routes are regiustered * Test update * term renaming and disabling API tests on MySQL * typo * Updated store type definition * Minor tweaks * Added tests * Removed error in app startup when content flaghging setup fails * Updated sync condition: * Flag message modal basE * added post preview * displaying options * Adde comment input * Updated tests and docs * finction rename * WIP * Updated tests * refactor * lint fix * MOved to data migration * lint fix * CI * added new migration mocks * Used setup for tests * some comment * Removed unnecesseery nil check * Form validation * WIP tests * WIP tests * WIP tests * fix: mock content flagging config selector with correct reasons format * fix: add mock for getContentFlaggingConfig in flag post modal test * Updated error code order in API docs * removed empty files * Added tests * lint fixes * minor tweak * lint fix * type fix * fixed test * nit * test enhancements * API WIP * API WIP * creating values * creating content flagging channel and properties * Able to save properties * Added another property field * WIP * WIP * Added validations * Added data validations and hidden post if confifgured to * lint fixes * Added API spec * Added some tests * Added tests for getContentReviewBot * test: add comprehensive tests for getContentReviewChannels function * Added more app layer tests * Added TestCanFlagPost * test: Add comprehensive tests for FlagPost function * Added all app layer tests * Removed a file that was reamoved downstream * test: add content flagging test file * test: add comprehensive tests for FlagContentRequest.IsValid method * Added model tests * test: add comprehensive tests for SqlPropertyValueStore.CreateMany * test: add comprehensive tests for flagPost() API function * Added API tests * linter fix * WIP * sent post flagging confirmation message * fixed i18n nissues * fixed i18n nissues * CI * Updated test * fix: reset contentFlaggingGroupId for test isolation in content flagging tests * removed cached group ID * removed debug log * review fixes * Used correct ot name * CI * Updated mobile text * Handled JSON error * fixerdf i18n * CI * Integrate flag post api (#33798) * WIP * WIP * Added API call * test: add test for Client4.flagPost API call in FlagPostModal * fix: remove userEvent.setup() from flag post modal test * test: wrap submit button click in act for proper state updates * Updated tests * lint fix * CI * Updated to allow special characters in comments * Handled empty comment * Used finally * CI * Fixed test * Spillage card integration (#33832) * Created getContentFlaggingFields API * created getPostPropertyValues API * WIP * Created useContentFlaggingFields hook * WIP * WIP * Added option to retain data for reviewers * Displayed deleted post's preview * DIsplayed all properties * Adding field name i18n * WIP - managing i18n able texts * Finished displaying all fields * Manual cleanup * lint fixes * team role filter logic fix * Fixed tests * created new API to fetch flagged posts * lint fix * Added new client methods * test: add comprehensive tests for content flagging APIs * Added new API tests * fixed openapi spec * Fixed DataSpillageReport tests * Fixed PostMarkdown test * Fixed PostPreviewPropertyRenderer test * Added metadata to card renderer * test fixes * Added no comment placeholder * Fixed test * refactor: improve test mocking for data spillage report component * test mock updates * Updated reducer * not resetting mocks * WIP * review fixes * CI * Fixed * fixes * Content flagging actions implementation (#33852) * Added view detail button * Created RemoveFlaggedMessageConfirmationModal modal * Added key and remove flag request modal * IMplemented delete flagged post * Handled edge cases of deleting flagged post * keep message * UI integration * Added WS event for post report update and handled deleted files of flagged post * Added error handling in keep/remove forms * i18n fixes * Updated OpenAPI specs * fixed types * fixed types * refactoring * Fixed tests * review fixes * Added new property translations * Improved test * fixed test * CI * fixes * CI * fixed a test * CI --------- * GH-34031: Add accessibility tests for Sidebar settings panel (#34041) * Add accessibility tests for Sidebar settings panel * Add aria snapshots and fix accessibility test expectations for sidebar settings * Update system_user_detail to enable editing of User Attributes - This adds a `updateUserCustomProfileAttributesValues` method to client4 to allow updating attributes for a given userID, as a match to getUserCustomProfilesAttributesValues - Fixed issue in saveCustomProfileAttributes where it used the version that did not take the passed in userId * Fixes for tests * Update saveCustomProfileAttribute mock in users.test.ts The `saveCustomProfileAttribute` test was still using the old getCustomProfileAttributeValuesRoute instead of the same route that `getCustomProfileAttributeValues` uses. * Adding e2e tests for editing User attributes (custom and system) in System Console * Fix testing issue with selector for 'Authentication Method' * Fixes from PR suggestions * Fix lint/export issues * Fix playwright lint issue halting e2e smoke test * Results from npm run prettier:fix (in e2e-tests/playwright folder) --------- Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Ibrahim Serdar Acikgoz <[email protected]> Co-authored-by: Alejandro García Montoro <[email protected]> Co-authored-by: Jesse Hallam <[email protected]> Co-authored-by: Amy Blais <[email protected]> Co-authored-by: Miguel de la Cruz <[email protected]> Co-authored-by: Miguel de la Cruz <[email protected]> Co-authored-by: Asaad Mahmood <[email protected]> Co-authored-by: Pablo Vélez <[email protected]> Co-authored-by: Eva Sarafianou <[email protected]> Co-authored-by: Jesse Hallam <[email protected]> Co-authored-by: sabril <[email protected]> Co-authored-by: Maria A Nunez <[email protected]> Co-authored-by: Harrison Healey <[email protected]> Co-authored-by: Harshil Sharma <[email protected]> Co-authored-by: Mudit Sharma <[email protected]> Co-authored-by: Hanzo Dev <[email protected]>
486 lines
17 KiB
TypeScript
486 lines
17 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {Page} from '@playwright/test';
|
|
import {Client4} from '@mattermost/client';
|
|
import {UserPropertyField, UserPropertyFieldPatch, FieldType} from '@mattermost/types/properties';
|
|
|
|
import {expect, ChannelsPage} from '@mattermost/playwright-lib';
|
|
|
|
// Common test data constants
|
|
export const TEST_PHONE = '555-123-4567';
|
|
export const TEST_UPDATED_PHONE = '555-987-6543';
|
|
export const TEST_URL = 'https://example.com';
|
|
export const TEST_UPDATED_URL = 'https://mattermost.com';
|
|
export const TEST_INVALID_URL = 'ftp://invalid-url';
|
|
export const TEST_VALID_URL = 'https://example2.com';
|
|
export const TEST_DEPARTMENT = 'Engineering';
|
|
export const TEST_UPDATED_DEPARTMENT = 'Product';
|
|
export const TEST_LOCATION = 'Remote';
|
|
export const TEST_UPDATED_LOCATION = 'Office';
|
|
export const TEST_TITLE = 'Software Engineer';
|
|
export const TEST_CHANGED_VALUE = 'Changed Value';
|
|
export const TEST_MESSAGE = 'Hello from the test user';
|
|
export const TEST_MESSAGE_OTHER = 'Hello from the other user';
|
|
|
|
export const TEST_LOCATION_OPTIONS = [
|
|
{name: 'Remote', color: '#00FFFF'},
|
|
{name: 'Office', color: '#FF00FF'},
|
|
{name: 'Hybrid', color: '#FFFF00'},
|
|
];
|
|
|
|
export const TEST_SKILLS_OPTIONS = [
|
|
{name: 'JavaScript', color: '#F0DB4F'},
|
|
{name: 'React', color: '#61DAFB'},
|
|
{name: 'Node.js', color: '#68A063'},
|
|
{name: 'Python', color: '#3776AB'},
|
|
];
|
|
|
|
/**
|
|
* Represents a custom profile attribute definition
|
|
*/
|
|
export type CustomProfileAttribute = {
|
|
name: string;
|
|
value?: string;
|
|
type: string;
|
|
options?: {name: string; color: string; sort_order?: number}[];
|
|
attrs?: {
|
|
value_type?: string;
|
|
visibility?: string;
|
|
options?: {name: string; color: string}[];
|
|
};
|
|
};
|
|
|
|
// Custom attribute definitions for user settings tests (with select/multiselect attributes)
|
|
export const userSettingsAttributes: CustomProfileAttribute[] = [
|
|
{
|
|
name: 'Department',
|
|
value: TEST_DEPARTMENT,
|
|
type: 'text',
|
|
},
|
|
{
|
|
name: 'Location',
|
|
type: 'select',
|
|
options: TEST_LOCATION_OPTIONS,
|
|
},
|
|
{
|
|
name: 'Skills',
|
|
type: 'multiselect',
|
|
options: TEST_SKILLS_OPTIONS,
|
|
},
|
|
{
|
|
name: 'Phone',
|
|
value: TEST_PHONE,
|
|
type: 'text',
|
|
attrs: {
|
|
value_type: 'phone',
|
|
},
|
|
},
|
|
{
|
|
name: 'Website',
|
|
value: TEST_URL,
|
|
type: 'text',
|
|
attrs: {
|
|
value_type: 'url',
|
|
},
|
|
},
|
|
];
|
|
|
|
// Custom attribute definitions for custom attributes tests (with text attributes and Title)
|
|
export const customAttributesTestData: CustomProfileAttribute[] = [
|
|
{
|
|
name: 'Department',
|
|
value: TEST_DEPARTMENT,
|
|
type: 'text',
|
|
},
|
|
{
|
|
name: 'Location',
|
|
value: TEST_LOCATION,
|
|
type: 'text',
|
|
},
|
|
{
|
|
name: 'Title',
|
|
value: TEST_TITLE,
|
|
type: 'text',
|
|
},
|
|
{
|
|
name: 'Phone',
|
|
value: TEST_PHONE,
|
|
type: 'text',
|
|
attrs: {
|
|
value_type: 'phone',
|
|
},
|
|
},
|
|
{
|
|
name: 'Website',
|
|
value: TEST_URL,
|
|
type: 'text',
|
|
attrs: {
|
|
value_type: 'url',
|
|
},
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Helper function to get field ID by name
|
|
* @param {Object} fieldsMap - Map of field IDs to field objects
|
|
* @param {string} name - The name of the field to find
|
|
* @returns {string} - The field ID
|
|
*/
|
|
export function getFieldIdByName(fieldsMap: Record<string, UserPropertyField>, name: string): string {
|
|
for (const [id, field] of Object.entries(fieldsMap)) {
|
|
if (field.name === name) {
|
|
return id;
|
|
}
|
|
}
|
|
throw new Error(`Could not find field ID for attribute: ${name}`);
|
|
}
|
|
|
|
/**
|
|
* Helper function to edit a text attribute
|
|
* @param {Page} page - The Playwright page object
|
|
* @param {Object} fieldsMap - Map of field IDs to field objects
|
|
* @param {string} attributeName - The name of the attribute to edit
|
|
* @param {string} newValue - The new value to set
|
|
*/
|
|
export async function editTextAttribute(
|
|
page: Page,
|
|
fieldsMap: Record<string, UserPropertyField>,
|
|
attributeName: string,
|
|
newValue: string,
|
|
): Promise<void> {
|
|
const fieldId = getFieldIdByName(fieldsMap, attributeName);
|
|
await page.locator(`text=${attributeName}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).click();
|
|
await page.locator(`#customAttribute_${fieldId}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}`).clear();
|
|
if (newValue) {
|
|
await page.locator(`#customAttribute_${fieldId}`).fill(newValue);
|
|
}
|
|
await page.locator('button:has-text("Save")').click();
|
|
}
|
|
|
|
/**
|
|
* Helper function to edit a select attribute
|
|
* @param {Page} page - The Playwright page object
|
|
* @param {Object} fieldsMap - Map of field IDs to field objects
|
|
* @param {string} attributeName - The name of the attribute to edit
|
|
* @param {number} optionIndex - The index of the option to select
|
|
*/
|
|
export async function editSelectAttribute(
|
|
page: Page,
|
|
fieldsMap: Record<string, UserPropertyField>,
|
|
attributeName: string,
|
|
optionIndex: number,
|
|
): Promise<void> {
|
|
const fieldId = getFieldIdByName(fieldsMap, attributeName);
|
|
await page.locator(`text=${attributeName}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).click();
|
|
await page.locator(`#customProfileAttribute_${fieldId}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customProfileAttribute_${fieldId}`).click();
|
|
await page.locator(`#react-select-2-option-${optionIndex}`).click();
|
|
await page.locator('button:has-text("Save")').click();
|
|
}
|
|
|
|
/**
|
|
* Helper function to edit a multiselect attribute
|
|
* @param {Page} page - The Playwright page object
|
|
* @param {Object} fieldsMap - Map of field IDs to field objects
|
|
* @param {string} attributeName - The name of the attribute to edit
|
|
* @param {Array<number>} optionIndices - The indices of the options to select
|
|
*/
|
|
export async function editMultiselectAttribute(
|
|
page: Page,
|
|
fieldsMap: Record<string, UserPropertyField>,
|
|
attributeName: string,
|
|
optionIndices: number[],
|
|
): Promise<void> {
|
|
const fieldId = getFieldIdByName(fieldsMap, attributeName);
|
|
await page.locator(`text=${attributeName}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customAttribute_${fieldId}Edit`).click();
|
|
|
|
for (const index of optionIndices) {
|
|
await page.waitForTimeout(500); // Wait for the dropdown to stabilize
|
|
await page.locator(`#customProfileAttribute_${fieldId}`).scrollIntoViewIfNeeded();
|
|
await page.locator(`#customProfileAttribute_${fieldId}`).click();
|
|
await page.locator(`#react-select-3-option-${index}`).click();
|
|
}
|
|
|
|
await page.locator('button:has-text("Save")').click();
|
|
await page.waitForTimeout(500); // Wait for save to complete
|
|
}
|
|
|
|
/**
|
|
* Helper function to verify an attribute exists in the profile settings
|
|
* @param {Page} page - The Playwright page object
|
|
* @param {Array} attributes - Array of attribute objects with name
|
|
*/
|
|
export async function verifyAttributesExistInSettings(page: Page, attributes: CustomProfileAttribute[]): Promise<void> {
|
|
for (const attribute of attributes) {
|
|
await page.locator(`text=${attribute.name}`).scrollIntoViewIfNeeded();
|
|
await expect(page.locator(`.user-settings:has-text("${attribute.name}")`)).toBeVisible();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to verify an attribute is displayed in the profile popover
|
|
* @param {ChannelsPage} channelsPage - The Playwright channels page object
|
|
* @param {string} attributeName - The name of the attribute to verify
|
|
* @param {string} attributeValue - The value of the attribute to verify
|
|
*/
|
|
export async function verifyAttributeInPopover(
|
|
channelsPage: ChannelsPage,
|
|
attributeName: string,
|
|
attributeValue: string,
|
|
): Promise<void> {
|
|
const popover = channelsPage.userProfilePopover.container;
|
|
|
|
// Check for the attribute name
|
|
const nameElement = popover.getByText(attributeName, {exact: false});
|
|
await expect(nameElement).toBeVisible();
|
|
|
|
// Check for the attribute value
|
|
const valueElement = popover.getByText(attributeValue, {exact: false});
|
|
await expect(valueElement).toBeVisible();
|
|
}
|
|
|
|
/**
|
|
* Helper function to verify an attribute is not displayed in the profile popover
|
|
* @param {ChannelsPage} channelsPage - The Playwright channels page object
|
|
* @param {string} attributeName - The name of the attribute to verify
|
|
*/
|
|
export async function verifyAttributeNotInPopover(channelsPage: ChannelsPage, attributeName: string): Promise<void> {
|
|
const popover = channelsPage.userProfilePopover.container;
|
|
|
|
// Check that the attribute name is not present
|
|
const nameElement = popover.getByText(attributeName, {exact: false});
|
|
await expect(nameElement).not.toBeVisible();
|
|
}
|
|
|
|
/**
|
|
* Updates the visibility property of a custom profile attribute field
|
|
* @param {Client4} adminClient - Admin API client
|
|
* @param {Object} fieldsMap - Map of field IDs to field objects
|
|
* @param {string} attributeName - The name of the attribute to update
|
|
* @param {string} visibility - The visibility value to set ('when_set', 'hidden', or 'always')
|
|
*/
|
|
export async function updateCustomProfileAttributeVisibility(
|
|
adminClient: Client4,
|
|
fieldsMap: Record<string, UserPropertyField>,
|
|
attributeName: string,
|
|
visibility: 'when_set' | 'hidden' | 'always',
|
|
): Promise<void> {
|
|
const fieldID = getFieldIdByName(fieldsMap, attributeName);
|
|
|
|
try {
|
|
// Update the visibility property
|
|
const updatedField = await adminClient.patchCustomProfileAttributeField(fieldID, {
|
|
// @ts-expect-error The type definition requires more properties than we need to set
|
|
attrs: {
|
|
visibility,
|
|
},
|
|
});
|
|
|
|
// Update the fieldsMap with the updated field
|
|
fieldsMap[updatedField.id] = updatedField;
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Failed to update visibility for attribute ${attributeName}:`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up custom profile attributes fields
|
|
* @param {Client4} adminClient - Admin API client
|
|
* @param {Array} attributes - Array of attribute objects with name and value
|
|
* @returns {Promise<Object>} - A promise that resolves to a map of field IDs to field objects
|
|
*/
|
|
export async function setupCustomProfileAttributeFields(
|
|
adminClient: Client4,
|
|
attributes: CustomProfileAttribute[],
|
|
): Promise<Record<string, UserPropertyField>> {
|
|
const fieldsMap: Record<string, UserPropertyField> = {};
|
|
|
|
// Create the attribute fields array
|
|
const attributeFields: UserPropertyFieldPatch[] = attributes.map((attr, index) => {
|
|
// Start with basic field properties
|
|
const field: UserPropertyFieldPatch = {
|
|
name: attr.name,
|
|
type: (attr.type as FieldType) || 'text',
|
|
// @ts-expect-error @mattermost/types needs to be updated
|
|
attrs: {
|
|
sort_order: index,
|
|
},
|
|
};
|
|
|
|
// Add options for select and multiselect fields
|
|
if ((attr.type === 'select' || attr.type === 'multiselect') && attr.options) {
|
|
// @ts-expect-error @mattermost/types needs to be updated
|
|
field.attrs.options = attr.options;
|
|
}
|
|
|
|
// Add any additional attributes if provided
|
|
if (attr.attrs) {
|
|
// @ts-expect-error @mattermost/types needs to be updated
|
|
field.attrs = {
|
|
...field.attrs,
|
|
...attr.attrs,
|
|
};
|
|
}
|
|
|
|
return field;
|
|
});
|
|
|
|
// Get existing fields
|
|
try {
|
|
const existingFields = await adminClient.getCustomProfileAttributeFields();
|
|
|
|
// If fields exist, use them
|
|
if (existingFields && existingFields.length > 0) {
|
|
for (const field of existingFields) {
|
|
fieldsMap[field.id] = field;
|
|
}
|
|
return fieldsMap;
|
|
}
|
|
} catch (error) {
|
|
// If request fails, continue to create new fields
|
|
// eslint-disable-next-line no-console
|
|
console.log('Error getting existing custom profile fields, will create new ones', error);
|
|
}
|
|
|
|
// Create fields sequentially
|
|
for (const field of attributeFields) {
|
|
try {
|
|
const createdField = await adminClient.createCustomProfileAttributeField(field);
|
|
fieldsMap[createdField.id] = createdField;
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Failed to create field ${field.name}:`, error);
|
|
}
|
|
}
|
|
|
|
return fieldsMap;
|
|
}
|
|
|
|
/**
|
|
* Sets up custom profile attribute values for the current user
|
|
* @param {Client4} userClient - User client object
|
|
* @param {Array} attributes - Array of attribute objects with name and value
|
|
* @param {Object} fields - Map of field IDs to field objects
|
|
*/
|
|
export async function setupCustomProfileAttributeValues(
|
|
userClient: Client4,
|
|
attributes: CustomProfileAttribute[],
|
|
fields: Record<string, UserPropertyField>,
|
|
): Promise<void> {
|
|
// Create a map of attribute values by field ID
|
|
const valuesByFieldId: Record<string, string | string[]> = {};
|
|
|
|
for (const attr of attributes) {
|
|
let fieldID = '';
|
|
|
|
// Find the field ID for this attribute name
|
|
for (const [id, field] of Object.entries(fields)) {
|
|
if (field.name === attr.name) {
|
|
fieldID = id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found a matching field, add it to our values object
|
|
if (fieldID && attr.value) {
|
|
valuesByFieldId[fieldID] = attr.value;
|
|
}
|
|
}
|
|
|
|
// Only make the API call if we have values to set
|
|
if (Object.keys(valuesByFieldId).length > 0) {
|
|
try {
|
|
await userClient.updateCustomProfileAttributeValues(valuesByFieldId);
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log('Failed to set attribute values:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up custom profile attribute values for a specific user (admin function)
|
|
* @param {Client4} adminClient - Admin client object
|
|
* @param {Array} attributes - Array of attribute objects with name and value
|
|
* @param {Object} fields - Map of field IDs to field objects
|
|
* @param {string} targetUserId - ID of the user to set values for
|
|
*/
|
|
export async function setupCustomProfileAttributeValuesForUser(
|
|
adminClient: Client4,
|
|
attributes: CustomProfileAttribute[],
|
|
fields: Record<string, UserPropertyField>,
|
|
targetUserId: string,
|
|
): Promise<void> {
|
|
// Create a map of attribute values by field ID
|
|
const valuesByFieldId: Record<string, string | string[]> = {};
|
|
|
|
for (const attr of attributes) {
|
|
let fieldID = '';
|
|
|
|
// Find the field ID for this attribute name
|
|
for (const [id, field] of Object.entries(fields)) {
|
|
if (field.name === attr.name) {
|
|
fieldID = id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found a matching field, add it to our values object
|
|
if (fieldID && attr.value) {
|
|
valuesByFieldId[fieldID] = attr.value;
|
|
}
|
|
}
|
|
|
|
// Only make the API call if we have values to set
|
|
if (Object.keys(valuesByFieldId).length > 0) {
|
|
try {
|
|
// Use the admin client method for updating other user's values
|
|
await adminClient.updateUserCustomProfileAttributesValues(targetUserId, valuesByFieldId);
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log('Failed to set attribute values for user:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes all custom profile attributes
|
|
* @param {Client4} adminClient - Admin API client
|
|
* @param {Object} attributes - Map of field IDs to field objects
|
|
*/
|
|
export async function deleteCustomProfileAttributes(
|
|
adminClient: Client4,
|
|
attributes: Record<string, UserPropertyField>,
|
|
): Promise<void> {
|
|
// Delete each field
|
|
for (const id of Object.keys(attributes)) {
|
|
try {
|
|
await adminClient.deleteCustomProfileAttributeField(id);
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Failed to delete field ${id}:`, error);
|
|
}
|
|
}
|
|
|
|
// Verify deletion was successful
|
|
try {
|
|
const response = await adminClient.getCustomProfileAttributeFields();
|
|
if (response && response.length > 0) {
|
|
// eslint-disable-next-line no-console
|
|
console.log('Warning: Not all custom profile attributes were deleted');
|
|
}
|
|
} catch (error) {
|
|
// eslint-disable-next-line no-console
|
|
console.log('Error checking if all fields were deleted:', error);
|
|
}
|
|
}
|