From 0fb862680e0d96d0bdda226cad84dac35b3a3ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andi=20P=C3=A4tzold?= Date: Mon, 15 Aug 2022 12:31:37 +0200 Subject: [PATCH] feat: add UIConfig and UserUIConfig [EXT-3738] (#1415) * feat: add UIConfig and UserUIConfig * feat: export & move new types * fix: remove `isFolder` & `folderId` * fix: remove roles prop from UserUIConfig * test: add unit tests * test: unit test REST endpoints * docs: update js docs * fix: add page field * feat: remove page prop Co-authored-by: Kado --- lib/adapters/REST/endpoints/index.ts | 10 +- lib/adapters/REST/endpoints/ui-config.ts | 31 +++++ lib/adapters/REST/endpoints/user-ui-config.ts | 31 +++++ lib/common-types.ts | 18 +++ lib/create-environment-api.ts | 30 +++++ lib/create-ui-config-api.ts | 62 ++++++++++ lib/create-user-ui-config-api.ts | 62 ++++++++++ lib/entities/index.ts | 12 +- lib/entities/ui-config.ts | 54 ++++++++ lib/entities/user-ui-config.ts | 53 ++++++++ lib/export-types.ts | 115 +++++++++--------- lib/plain/common-types.ts | 18 +++ lib/plain/plain-client.ts | 8 ++ .../adapters/REST/endpoints/ui-config-test.js | 36 ++++++ .../REST/endpoints/user-ui-config-test.js | 36 ++++++ test/unit/create-environment-api-test.js | 16 +++ test/unit/create-ui-config-api-test.js | 39 ++++++ test/unit/create-user-ui-config-api-test.js | 39 ++++++ test/unit/entities/ui-config.js | 20 +++ test/unit/entities/user-ui-config.js | 20 +++ test/unit/mocks/entities.js | 30 +++++ 21 files changed, 675 insertions(+), 65 deletions(-) create mode 100644 lib/adapters/REST/endpoints/ui-config.ts create mode 100644 lib/adapters/REST/endpoints/user-ui-config.ts create mode 100644 lib/create-ui-config-api.ts create mode 100644 lib/create-user-ui-config-api.ts create mode 100644 lib/entities/ui-config.ts create mode 100644 lib/entities/user-ui-config.ts create mode 100644 test/unit/adapters/REST/endpoints/ui-config-test.js create mode 100644 test/unit/adapters/REST/endpoints/user-ui-config-test.js create mode 100644 test/unit/create-ui-config-api-test.js create mode 100644 test/unit/create-user-ui-config-api-test.js create mode 100644 test/unit/entities/ui-config.js create mode 100644 test/unit/entities/user-ui-config.js diff --git a/lib/adapters/REST/endpoints/index.ts b/lib/adapters/REST/endpoints/index.ts index 8f6060d0c2..febf6d0e09 100644 --- a/lib/adapters/REST/endpoints/index.ts +++ b/lib/adapters/REST/endpoints/index.ts @@ -3,11 +3,11 @@ import * as AppAction from './app-action' import * as AppActionCall from './app-action-call' import * as AppBundle from './app-bundle' import * as AppDefinition from './app-definition' -import * as AppInstallation from './app-installation' -import * as AppUpload from './app-upload' import * as AppDetails from './app-details' +import * as AppInstallation from './app-installation' import * as AppSignedRequest from './app-signed-request' import * as AppSigningSecret from './app-signing-secret' +import * as AppUpload from './app-upload' import * as Asset from './asset' import * as AssetKey from './asset-key' import * as BulkAction from './bulk-action' @@ -38,12 +38,14 @@ import * as Task from './task' import * as Team from './team' import * as TeamMembership from './team-membership' import * as TeamSpaceMembership from './team-space-membership' +import * as UIConfig from './ui-config' import * as Upload from './upload' import * as Usage from './usage' import * as User from './user' +import * as UserUIConfig from './user-ui-config' import * as Webhook from './webhook' -import * as WorkflowDefinition from './workflow-definition' import * as Workflow from './workflow' +import * as WorkflowDefinition from './workflow-definition' import * as WorkflowsChangelog from './workflows-changelog' export default { @@ -87,9 +89,11 @@ export default { Team, TeamMembership, TeamSpaceMembership, + UIConfig, Upload, Usage, User, + UserUIConfig, Webhook, WorkflowDefinition, Workflow, diff --git a/lib/adapters/REST/endpoints/ui-config.ts b/lib/adapters/REST/endpoints/ui-config.ts new file mode 100644 index 0000000000..ded5a83197 --- /dev/null +++ b/lib/adapters/REST/endpoints/ui-config.ts @@ -0,0 +1,31 @@ +import type { AxiosInstance } from 'contentful-sdk-core' +import { SetOptional } from 'type-fest' +import { GetUIConfigParams } from '../../../common-types' +import { UIConfigProps } from '../../../entities/ui-config' +import { RestEndpoint } from '../types' +import * as raw from './raw' +import copy from 'fast-copy' + +const getUrl = (params: GetUIConfigParams) => + `/spaces/${params.spaceId}/environments/${params.environmentId}/ui_config` + +export const get: RestEndpoint<'UIConfig', 'get'> = ( + http: AxiosInstance, + params: GetUIConfigParams +) => { + return raw.get(http, getUrl(params)) +} + +export const update: RestEndpoint<'UIConfig', 'update'> = ( + http: AxiosInstance, + params: GetUIConfigParams, + rawData: UIConfigProps +) => { + const data: SetOptional = copy(rawData) + delete data.sys + return raw.put(http, getUrl(params), data, { + headers: { + 'X-Contentful-Version': rawData.sys.version ?? 0, + }, + }) +} diff --git a/lib/adapters/REST/endpoints/user-ui-config.ts b/lib/adapters/REST/endpoints/user-ui-config.ts new file mode 100644 index 0000000000..204eb573d9 --- /dev/null +++ b/lib/adapters/REST/endpoints/user-ui-config.ts @@ -0,0 +1,31 @@ +import type { AxiosInstance } from 'contentful-sdk-core' +import copy from 'fast-copy' +import { SetOptional } from 'type-fest' +import { GetUserUIConfigParams } from '../../../common-types' +import { UserUIConfigProps } from '../../../entities/user-ui-config' +import { RestEndpoint } from '../types' +import * as raw from './raw' + +const getUrl = (params: GetUserUIConfigParams) => + `/spaces/${params.spaceId}/environments/${params.environmentId}/ui_config/me` + +export const get: RestEndpoint<'UserUIConfig', 'get'> = ( + http: AxiosInstance, + params: GetUserUIConfigParams +) => { + return raw.get(http, getUrl(params)) +} + +export const update: RestEndpoint<'UserUIConfig', 'update'> = ( + http: AxiosInstance, + params: GetUserUIConfigParams, + rawData: UserUIConfigProps +) => { + const data: SetOptional = copy(rawData) + delete data.sys + return raw.put(http, getUrl(params), data, { + headers: { + 'X-Contentful-Version': rawData.sys.version ?? 0, + }, + }) +} diff --git a/lib/common-types.ts b/lib/common-types.ts index 41b8636c53..e913d99dc9 100644 --- a/lib/common-types.ts +++ b/lib/common-types.ts @@ -113,6 +113,8 @@ import { WorkflowsChangelogEntryProps, WorkflowsChangelogQueryOptions, } from './entities/workflows-changelog-entry' +import { UIConfigProps } from './entities/ui-config' +import { UserUIConfigProps } from './entities/user-ui-config' export interface DefaultElements { toPlainObject(): TPlainObject @@ -534,6 +536,9 @@ type MRInternal = { (opts: MROpts<'TeamSpaceMembership', 'update', UA>): MRReturn<'TeamSpaceMembership', 'update'> (opts: MROpts<'TeamSpaceMembership', 'delete', UA>): MRReturn<'TeamSpaceMembership', 'delete'> + (opts: MROpts<'UIConfig', 'get', UA>): MRReturn<'UIConfig', 'get'> + (opts: MROpts<'UIConfig', 'update', UA>): MRReturn<'UIConfig', 'update'> + (opts: MROpts<'Upload', 'get', UA>): MRReturn<'Entry', 'get'> (opts: MROpts<'Upload', 'create', UA>): MRReturn<'Entry', 'create'> (opts: MROpts<'Upload', 'delete', UA>): MRReturn<'Entry', 'delete'> @@ -547,6 +552,9 @@ type MRInternal = { (opts: MROpts<'User', 'getForOrganization', UA>): MRReturn<'User', 'getForOrganization'> (opts: MROpts<'User', 'getManyForOrganization', UA>): MRReturn<'User', 'getManyForOrganization'> + (opts: MROpts<'UserUIConfig', 'get', UA>): MRReturn<'UserUIConfig', 'update'> + (opts: MROpts<'UserUIConfig', 'update', UA>): MRReturn<'UserUIConfig', 'update'> + (opts: MROpts<'Webhook', 'get', UA>): MRReturn<'Webhook', 'get'> (opts: MROpts<'Webhook', 'getMany', UA>): MRReturn<'Webhook', 'getMany'> (opts: MROpts<'Webhook', 'getCallDetails', UA>): MRReturn<'Webhook', 'getCallDetails'> @@ -1355,6 +1363,10 @@ export type MRActions = { } delete: { params: GetTeamSpaceMembershipParams; return: any } } + UIConfig: { + get: { params: GetUIConfigParams; return: UIConfigProps } + update: { params: GetUIConfigParams; payload: UIConfigProps; return: UIConfigProps } + } Upload: { get: { params: GetSpaceParams & { uploadId: string }; return: any } create: { @@ -1384,6 +1396,10 @@ export type MRActions = { return: CollectionProp } } + UserUIConfig: { + get: { params: GetUserUIConfigParams; return: UserUIConfigProps } + update: { params: GetUserUIConfigParams; payload: UserUIConfigProps; return: UserUIConfigProps } + } Webhook: { get: { params: GetWebhookParams; return: WebhookProps } getMany: { params: GetSpaceParams & QueryParams; return: CollectionProp } @@ -1561,6 +1577,8 @@ export type GetWorkflowDefinitionParams = GetSpaceEnvironmentParams & { export type GetWorkflowParams = GetSpaceEnvironmentParams & { workflowId: string } +export type GetUIConfigParams = GetSpaceEnvironmentParams +export type GetUserUIConfigParams = GetUIConfigParams export type QueryParams = { query?: QueryOptions } export type PaginationQueryParams = { query?: PaginationQueryOptions } diff --git a/lib/create-environment-api.ts b/lib/create-environment-api.ts index ea0065a062..d98eaa12b5 100644 --- a/lib/create-environment-api.ts +++ b/lib/create-environment-api.ts @@ -41,6 +41,8 @@ import { EnvironmentProps } from './entities/environment' import type { CreateExtensionProps } from './entities/extension' import type { CreateLocaleProps } from './entities/locale' import { TagVisibility, wrapTag, wrapTagCollection } from './entities/tag' +import { wrapUIConfig } from './entities/ui-config' +import { wrapUserUIConfig } from './entities/user-ui-config' /** * @private @@ -2104,5 +2106,33 @@ export default function createEnvironmentApi(makeRequest: MakeRequest) { }, }).then((data) => wrapReleaseActionCollection(makeRequest, data)) }, + + async getUIConfig() { + const raw: EnvironmentProps = this.toPlainObject() + + const data = await makeRequest({ + entityType: 'UIConfig', + action: 'get', + params: { + spaceId: raw.sys.space.sys.id, + environmentId: raw.sys.id, + }, + }) + return wrapUIConfig(makeRequest, data) + }, + + async getUserUIConfig() { + const raw: EnvironmentProps = this.toPlainObject() + + const data = await makeRequest({ + entityType: 'UserUIConfig', + action: 'get', + params: { + spaceId: raw.sys.space.sys.id, + environmentId: raw.sys.id, + }, + }) + return wrapUserUIConfig(makeRequest, data) + }, } } diff --git a/lib/create-ui-config-api.ts b/lib/create-ui-config-api.ts new file mode 100644 index 0000000000..5fcdd1221a --- /dev/null +++ b/lib/create-ui-config-api.ts @@ -0,0 +1,62 @@ +import { MakeRequest } from './common-types' +import entities from './entities' +import { UIConfig } from './entities/ui-config' + +/** + * @private + */ +export type ContentfulUIConfigApi = ReturnType + +/** + * @private + */ +export default function createUIConfigApi(makeRequest: MakeRequest) { + const { wrapUIConfig } = entities.uiConfig + + const getParams = (self: UIConfig) => { + const uiConfig = self.toPlainObject() + + return { + params: { + spaceId: uiConfig.sys.space.sys.id, + environmentId: uiConfig.sys.environment.sys.id, + }, + raw: uiConfig, + } + } + + return { + /** + * Sends an update to the server with any changes made to the object's properties + * @return Object returned from the server with updated changes. + * @example ```javascript + * const contentful = require('contentful-management') + * + * const client = contentful.createClient({ + * accessToken: '' + * }) + * + * client.getSpace('') + * .then((space) => space.getEnvironment('')) + * .then((environment) => environment.getUIConfig()) + * .then((uiConfig) => { + * uiConfig.entryListViews = [...] + * return uiConfig.update() + * }) + * .then((uiConfig) => console.log(`UIConfig updated.`)) + * .catch(console.error) + * ``` + */ + update: async function update() { + const { raw, params } = getParams(this) + + const data = await makeRequest({ + entityType: 'UIConfig', + action: 'update', + params, + payload: raw, + }) + return wrapUIConfig(makeRequest, data) + }, + } +} diff --git a/lib/create-user-ui-config-api.ts b/lib/create-user-ui-config-api.ts new file mode 100644 index 0000000000..9030fd0411 --- /dev/null +++ b/lib/create-user-ui-config-api.ts @@ -0,0 +1,62 @@ +import { MakeRequest } from './common-types' +import entities from './entities' +import { UserUIConfig } from './entities/user-ui-config' + +/** + * @private + */ +export type ContentfulUIConfigApi = ReturnType + +/** + * @private + */ +export default function createUserUIConfigApi(makeRequest: MakeRequest) { + const { wrapUserUIConfig } = entities.userUIConfig + + const getParams = (self: UserUIConfig) => { + const userUIConfig = self.toPlainObject() + + return { + params: { + spaceId: userUIConfig.sys.space.sys.id, + environmentId: userUIConfig.sys.environment.sys.id, + }, + raw: userUIConfig, + } + } + + return { + /** + * Sends an update to the server with any changes made to the object's properties + * @return Object returned from the server with updated changes. + * @example ```javascript + * const contentful = require('contentful-management') + * + * const client = contentful.createClient({ + * accessToken: '' + * }) + * + * client.getSpace('') + * .then((space) => space.getEnvironment('')) + * .then((environment) => environment.getUserUIConfig()) + * .then((uiConfig) => { + * uiConfig.entryListViews = [...] + * return uiConfig.update() + * }) + * .then((uiConfig) => console.log(`UserUIConfig updated.`)) + * .catch(console.error) + * ``` + */ + update: async function update() { + const { raw, params } = getParams(this) + + const data = await makeRequest({ + entityType: 'UserUIConfig', + action: 'update', + params, + payload: raw, + }) + return wrapUserUIConfig(makeRequest, data) + }, + } +} diff --git a/lib/entities/index.ts b/lib/entities/index.ts index 986cfc02be..6d7d6c0674 100644 --- a/lib/entities/index.ts +++ b/lib/entities/index.ts @@ -3,11 +3,11 @@ import * as appAction from './app-action' import * as appActionCall from './app-action-call' import * as appBundle from './app-bundle' import * as appDefinition from './app-definition' -import * as appInstallation from './app-installation' -import * as appUpload from './app-upload' import * as appDetails from './app-details' +import * as appInstallation from './app-installation' import * as appSignedRequest from './app-signed-request' import * as appSigningSecret from './app-signing-secret' +import * as appUpload from './app-upload' import * as asset from './asset' import * as assetKey from './asset-key' import * as bulkAction from './bulk-action' @@ -32,14 +32,16 @@ import * as snapshot from './snapshot' import * as space from './space' import * as spaceMember from './space-member' import * as spaceMembership from './space-membership' -import * as team from './team' -import * as teamMembership from './team-membership' import * as tag from './tag' import * as task from './task' +import * as team from './team' +import * as teamMembership from './team-membership' import * as teamSpaceMembership from './team-space-membership' +import * as uiConfig from './ui-config' import * as upload from './upload' import * as usage from './usage' import * as user from './user' +import * as userUIConfig from './user-ui-config' import * as webhook from './webhook' import * as workflowDefinition from './workflow-definition' @@ -83,9 +85,11 @@ export default { team, teamMembership, teamSpaceMembership, + uiConfig, upload, usage, user, + userUIConfig, webhook, workflowDefinition, } diff --git a/lib/entities/ui-config.ts b/lib/entities/ui-config.ts new file mode 100644 index 0000000000..f9eadf2d3f --- /dev/null +++ b/lib/entities/ui-config.ts @@ -0,0 +1,54 @@ +import { freezeSys, toPlainObject } from 'contentful-sdk-core' +import copy from 'fast-copy' +import { BasicMetaSysProps, DefaultElements, MakeRequest, SysLink } from '../common-types' +import createUIConfigApi from '../create-ui-config-api' +import enhanceWithMethods from '../enhance-with-methods' + +export type UIConfigProps = { + /** + * System metadata + */ + sys: UIConfigSysProps + + assetListViews: ViewFolder[] + entryListViews: ViewFolder[] +} + +export interface UIConfigSysProps extends BasicMetaSysProps { + space: SysLink + environment: SysLink +} + +interface ViewFolder { + id: string + title: string + views: View[] +} + +interface View { + id: string + title: string + order?: { + fieldId: string + direction: 'ascending' | 'descending' + } + displayedFieldIds?: string[] + contentTypeId: string | null + searchText?: string + searchFilters?: [string, string, string][] + roles?: string[] +} + +export interface UIConfig extends UIConfigProps, DefaultElements {} + +/** + * @private + * @param makeRequest - function to make requests via an adapter + * @param data - Raw data + * @return Wrapped UIConfig + */ +export function wrapUIConfig(makeRequest: MakeRequest, data: UIConfigProps) { + const user = toPlainObject(copy(data)) + const userWithMethods = enhanceWithMethods(user, createUIConfigApi(makeRequest)) + return freezeSys(userWithMethods) +} diff --git a/lib/entities/user-ui-config.ts b/lib/entities/user-ui-config.ts new file mode 100644 index 0000000000..5bbb6850f6 --- /dev/null +++ b/lib/entities/user-ui-config.ts @@ -0,0 +1,53 @@ +import { freezeSys, toPlainObject } from 'contentful-sdk-core' +import copy from 'fast-copy' +import { BasicMetaSysProps, DefaultElements, MakeRequest, SysLink } from '../common-types' +import createUserUIConfigApi from '../create-user-ui-config-api' +import enhanceWithMethods from '../enhance-with-methods' + +export type UserUIConfigProps = { + /** + * System metadata + */ + sys: UserUIConfigSysProps + + assetListViews: ViewFolder[] + entryListViews: ViewFolder[] +} + +export interface UserUIConfigSysProps extends BasicMetaSysProps { + space: SysLink + environment: SysLink +} + +interface ViewFolder { + id: string + title: string + views: View[] +} + +interface View { + id: string + title: string + order?: { + fieldId: string + direction: 'ascending' | 'descending' + } + displayedFieldIds?: string[] + contentTypeId: string | null + searchText?: string + searchFilters?: [string, string, string][] +} + +export interface UserUIConfig extends UserUIConfigProps, DefaultElements {} + +/** + * @private + * @param makeRequest - function to make requests via an adapter + * @param data - Raw data + * @return Wrapped UserUIConfig + */ +export function wrapUserUIConfig(makeRequest: MakeRequest, data: UserUIConfigProps) { + const user = toPlainObject(copy(data)) + const userWithMethods = enhanceWithMethods(user, createUserUIConfigApi(makeRequest)) + return freezeSys(userWithMethods) +} diff --git a/lib/export-types.ts b/lib/export-types.ts index 46686f3094..358f5fd222 100644 --- a/lib/export-types.ts +++ b/lib/export-types.ts @@ -1,12 +1,12 @@ export * from './common-types' - +export type { ApiKey, ApiKeyProps, CreateApiKeyProps } from './entities/api-key' export type { AppAction, + AppActionCategoryProps, + AppActionCategoryType, + AppActionParameterDefinition, AppActionProps, CreateAppActionProps, - AppActionParameterDefinition, - AppActionCategoryType, - AppActionCategoryProps, } from './entities/app-action' export type { AppActionCall, @@ -15,34 +15,32 @@ export type { } from './entities/app-action-call' export type { AppBundle, - AppBundleProps, AppBundleFile, + AppBundleProps, CreateAppBundleProps, } from './entities/app-bundle' -export type { ApiKey, ApiKeyProps, CreateApiKeyProps } from './entities/api-key' -export type { AppUploadProps, AppUpload } from './entities/app-upload' export type { AppDefinition, AppDefinitionProps, AppLocation, - SimpleLocation, + CreateAppDefinitionProps, EntryFieldLocation, - PageLocation, NavigationItem, - CreateAppDefinitionProps, + PageLocation, + SimpleLocation, } from './entities/app-definition' -export type { - AppInstallation, - AppInstallationProps, - CreateAppInstallationProps, -} from './entities/app-installation' export type { AppDetails, AppDetailsProps, + AppIcon, CreateAppDetailsProps, IconType, - AppIcon, } from './entities/app-details' +export type { + AppInstallation, + AppInstallationProps, + CreateAppInstallationProps, +} from './entities/app-installation' export type { AppSignedRequest, AppSignedRequestProps, @@ -53,17 +51,18 @@ export type { AppSigningSecretProps, CreateAppSigningSecretProps, } from './entities/app-signing-secret' -export type { Asset, AssetProps, CreateAssetProps, AssetFileProp } from './entities/asset' +export type { AppUpload, AppUploadProps } from './entities/app-upload' +export type { Asset, AssetFileProp, AssetProps, CreateAssetProps } from './entities/asset' export type { AssetKey, AssetKeyProps, CreateAssetKeyProps } from './entities/asset-key' export type { BulkAction, + BulkActionPayload, + BulkActionProps, BulkActionPublishPayload, - BulkActionUnpublishPayload, - BulkActionValidatePayload, BulkActionStatus, - BulkActionPayload, BulkActionType, - BulkActionProps, + BulkActionUnpublishPayload, + BulkActionValidatePayload, } from './entities/bulk-action' export type { Comment, @@ -73,50 +72,50 @@ export type { } from './entities/comment' export type { ContentType, + ContentTypeMetadata, ContentTypeProps, CreateContentTypeProps, - ContentTypeMetadata, } from './entities/content-type' export type { ContentFields, ContentTypeFieldValidation } from './entities/content-type-fields' export type { - EditorInterface, - EditorInterfaceProps, Control, - GroupControl, Editor, + EditorInterface, + EditorInterfaceProps, EditorLayoutItem, FieldGroupItem, FieldItem, + GroupControl, SidebarItem, } from './entities/editor-interface' -export type { FieldType } from './entities/field-type' -export type { - ParameterDefinition, - DefinedParameters, - FreeFormParameters, -} from './entities/widget-parameters' -export type { Entry, EntryProps, CreateEntryProps } from './entities/entry' -export type { Environment, EnvironmentProps, CreateEnvironmentProps } from './entities/environment' +export type { CreateEntryProps, Entry, EntryProps } from './entities/entry' +export type { CreateEnvironmentProps, Environment, EnvironmentProps } from './entities/environment' export type { + CreateEnvironmentAliasProps, EnvironmentAlias, EnvironmentAliasProps, - CreateEnvironmentAliasProps, } from './entities/environment-alias' -export type { Locale, LocaleProps, CreateLocaleProps } from './entities/locale' +export type { + CreateExtensionProps as CreateUIExtensionProps, + Extension as UIExtension, + ExtensionProps as UIExtensionProps, +} from './entities/extension' +export type { FieldType } from './entities/field-type' +export type { CreateLocaleProps, Locale, LocaleProps } from './entities/locale' export type { Organization, OrganizationProp } from './entities/organization' export type { + CreateOrganizationInvitationProps, OrganizationInvitation, OrganizationInvitationProps, - CreateOrganizationInvitationProps, } from './entities/organization-invitation' export type { OrganizationMembership, OrganizationMembershipProps, } from './entities/organization-membership' export type { + CreatePersonalAccessTokenProps, PersonalAccessToken, PersonalAccessTokenProp, - CreatePersonalAccessTokenProps, } from './entities/personal-access-token' export type { PreviewApiKey, PreviewApiKeyProps } from './entities/preview-api-key' export type { @@ -134,7 +133,7 @@ export type { ReleaseActionSysProps, ReleaseActionTypes, } from './entities/release-action' -export type { Role, RoleProps, CreateRoleProps } from './entities/role' +export type { CreateRoleProps, Role, RoleProps } from './entities/role' export type { ScheduledAction, ScheduledActionProps, @@ -144,39 +143,36 @@ export type { Snapshot, SnapshotProps } from './entities/snapshot' export type { Space, SpaceProps } from './entities/space' export type { SpaceMember, SpaceMemberProps } from './entities/space-member' export type { + CreateSpaceMembershipProps, SpaceMembership, SpaceMembershipProps, - CreateSpaceMembershipProps, } from './entities/space-membership' -export type { Team, TeamProps, CreateTeamProps } from './entities/team' +export type { CreateTagProps, Tag, TagProps, TagVisibility } from './entities/tag' +export type { CreateTaskProps, Task, TaskProps, UpdateTaskProps } from './entities/task' +export type { CreateTeamProps, Team, TeamProps } from './entities/team' export type { + CreateTeamMembershipProps, TeamMembership, TeamMembershipProps, - CreateTeamMembershipProps, } from './entities/team-membership' -export type { Tag, TagProps, CreateTagProps, TagVisibility } from './entities/tag' -export type { Task, TaskProps, CreateTaskProps, UpdateTaskProps } from './entities/task' export type { + CreateTeamSpaceMembershipProps, TeamSpaceMembership, TeamSpaceMembershipProps, - CreateTeamSpaceMembershipProps, } from './entities/team-space-membership' -export type { - Extension as UIExtension, - ExtensionProps as UIExtensionProps, - CreateExtensionProps as CreateUIExtensionProps, -} from './entities/extension' +export type { UIConfig, UIConfigProps } from './entities/ui-config' export type { Upload, UploadProps } from './entities/upload' export type { Usage, UsageProps } from './entities/usage' export type { User, UserProps } from './entities/user' +export type { UserUIConfig, UserUIConfigProps } from './entities/user-ui-config' export type { - WebHooks, - WebhookProps, - WebhookCallRequest, - WebhookTransformation, - WebhookFilter, CreateWebhooksProps, UpdateWebhookProps, + WebhookCallRequest, + WebhookFilter, + WebhookProps, + WebHooks, + WebhookTransformation, } from './entities/webhook' export type { // General typings (props, params, options) @@ -209,13 +205,16 @@ export type { WorkflowStepAppAction, } from './entities/workflow-definition' export type { - Workflow, - WorkflowProps, + DefinedParameters, + FreeFormParameters, + ParameterDefinition, +} from './entities/widget-parameters' +export type { CreateWorkflowProps, UpdateWorkflowProps, - CreateWorkflowParams, - UpdateWorkflowParams, DeleteWorkflowParams, + Workflow, + WorkflowProps, WorkflowQueryOptions, } from './entities/workflow' export type { diff --git a/lib/plain/common-types.ts b/lib/plain/common-types.ts index dce284fcb0..5a33cb6978 100644 --- a/lib/plain/common-types.ts +++ b/lib/plain/common-types.ts @@ -37,6 +37,8 @@ import { CursorPaginatedCollectionProp, GetWorkflowDefinitionParams, GetAppActionsForEnvParams, + GetUserUIConfigParams, + GetUIConfigParams, } from '../common-types' import { ApiKeyProps, CreateApiKeyProps } from '../entities/api-key' import { @@ -156,6 +158,8 @@ import { WorkflowsChangelogEntryProps, WorkflowsChangelogQueryOptions, } from '../entities/workflows-changelog-entry' +import { UserUIConfigProps } from '../entities/user-ui-config' +import { UIConfigProps } from '../entities/ui-config' export type PlainClientAPI = { raw: { @@ -894,6 +898,20 @@ export type PlainClientAPI = { ): Promise delete(params: OptionalDefaults): Promise } + uiConfig: { + get(params: OptionalDefaults): Promise + update( + params: OptionalDefaults, + rawData: UIConfigProps + ): Promise + } + userUIConfig: { + get(params: OptionalDefaults): Promise + update( + params: OptionalDefaults, + rawData: UserUIConfigProps + ): Promise + } } export type AlphaWorkflowExtension = { diff --git a/lib/plain/plain-client.ts b/lib/plain/plain-client.ts index 42e9bccb2e..b4b5e3bc35 100644 --- a/lib/plain/plain-client.ts +++ b/lib/plain/plain-client.ts @@ -386,6 +386,14 @@ export const createPlainClient = ( update: wrap(wrapParams, 'TeamSpaceMembership', 'update'), delete: wrap(wrapParams, 'TeamSpaceMembership', 'delete'), }, + uiConfig: { + get: wrap(wrapParams, 'UIConfig', 'get'), + update: wrap(wrapParams, 'UIConfig', 'update'), + }, + userUIConfig: { + get: wrap(wrapParams, 'UserUIConfig', 'get'), + update: wrap(wrapParams, 'UserUIConfig', 'update'), + }, ...addAlphaFeatures(makeRequest, defaults, alphaFeatures), } } diff --git a/test/unit/adapters/REST/endpoints/ui-config-test.js b/test/unit/adapters/REST/endpoints/ui-config-test.js new file mode 100644 index 0000000000..88d6c0dd45 --- /dev/null +++ b/test/unit/adapters/REST/endpoints/ui-config-test.js @@ -0,0 +1,36 @@ +import { expect } from 'chai' +import { describe, test } from 'mocha' +import { wrapUIConfig } from '../../../../../lib/entities/ui-config' +import { cloneMock } from '../../../mocks/entities' +import setupRestAdapter from '../helpers/setupRestAdapter' + +function setup(promise, params = {}) { + return { + ...setupRestAdapter(promise, params), + entityMock: cloneMock('uiConfig'), + } +} + +describe('Rest UIConfig', () => { + test('UIConfig update works', async () => { + const { httpMock, adapterMock } = setup() + const entityMock = cloneMock('uiConfig') + entityMock.sys.version = 2 + const entity = wrapUIConfig((...args) => adapterMock.makeRequest(...args), entityMock) + entity.entryListViews[0] = 'view' + + return entity.update().then((response) => { + expect(response.toPlainObject, 'response is wrapped').to.be.ok + expect(httpMock.put.args[0][1].entryListViews[0]).equals('view', 'metadata is sent') + expect(httpMock.put.args[0][2].headers['X-Contentful-Version']).equals( + 2, + 'version header is sent' + ) + return { + httpMock, + entityMock, + response, + } + }) + }) +}) diff --git a/test/unit/adapters/REST/endpoints/user-ui-config-test.js b/test/unit/adapters/REST/endpoints/user-ui-config-test.js new file mode 100644 index 0000000000..2b9e621759 --- /dev/null +++ b/test/unit/adapters/REST/endpoints/user-ui-config-test.js @@ -0,0 +1,36 @@ +import { expect } from 'chai' +import { describe, test } from 'mocha' +import { wrapUIConfig } from '../../../../../lib/entities/ui-config' +import { cloneMock } from '../../../mocks/entities' +import setupRestAdapter from '../helpers/setupRestAdapter' + +function setup(promise, params = {}) { + return { + ...setupRestAdapter(promise, params), + entityMock: cloneMock('userUIConfig'), + } +} + +describe('Rest UserUIConfig', () => { + test('UIConfig update works', async () => { + const { httpMock, adapterMock } = setup() + const entityMock = cloneMock('userUIConfig') + entityMock.sys.version = 2 + const entity = wrapUIConfig((...args) => adapterMock.makeRequest(...args), entityMock) + entity.entryListViews[0] = 'view' + + return entity.update().then((response) => { + expect(response.toPlainObject, 'response is wrapped').to.be.ok + expect(httpMock.put.args[0][1].entryListViews[0]).equals('view', 'metadata is sent') + expect(httpMock.put.args[0][2].headers['X-Contentful-Version']).equals( + 2, + 'version header is sent' + ) + return { + httpMock, + entityMock, + response, + } + }) + }) +}) diff --git a/test/unit/create-environment-api-test.js b/test/unit/create-environment-api-test.js index 04112ef922..c4996ebe78 100644 --- a/test/unit/create-environment-api-test.js +++ b/test/unit/create-environment-api-test.js @@ -593,6 +593,22 @@ describe('A createEnvironmentApi', () => { expect(r).to.equals(error) }) }) + + test('API call getUIConfig', async () => { + const uiConfig = cloneMock('uiConfig') + const { api } = setup(Promise.resolve(cloneMock('uiConfig'))) + return api.getUIConfig().then((r) => { + expect(r).eql(uiConfig) + }) + }) + + test('API call getUserUIConfig', async () => { + const userUIConfig = cloneMock('userUIConfig') + const { api } = setup(Promise.resolve(cloneMock('userUIConfig'))) + return api.getUserUIConfig().then((r) => { + expect(r).eql(userUIConfig) + }) + }) }) // Embargoed Assets diff --git a/test/unit/create-ui-config-api-test.js b/test/unit/create-ui-config-api-test.js new file mode 100644 index 0000000000..24616fd0d4 --- /dev/null +++ b/test/unit/create-ui-config-api-test.js @@ -0,0 +1,39 @@ +import { afterEach, describe, test } from 'mocha' +import createUIConfigApi, { + __RewireAPI__ as createUIConfigApiRewireApi, +} from '../../lib/create-ui-config-api' +import { wrapUIConfig } from '../../lib/entities/ui-config' +import { cloneMock } from './mocks/entities' +import setupMakeRequest from './mocks/makeRequest' +import { entityUpdateTest, failingVersionActionTest } from './test-creators/instance-entity-methods' + +function setup(promise) { + const makeRequest = setupMakeRequest(promise) + const uiConfigMock = cloneMock('uiConfig') + const api = createUIConfigApi(makeRequest) + api.toPlainObject = () => uiConfigMock + return { + api, + makeRequest, + entityMock: uiConfigMock, + } +} + +describe('createUIConfigApi', () => { + afterEach(() => { + createUIConfigApiRewireApi.__ResetDependency__('entities') + }) + + test('UIConfig update', async () => { + return entityUpdateTest(setup, { + wrapperMethod: wrapUIConfig, + }) + }) + + test('UIConfig update fails', async () => { + return failingVersionActionTest(setup, { + wrapperMethod: wrapUIConfig, + actionMethod: 'update', + }) + }) +}) diff --git a/test/unit/create-user-ui-config-api-test.js b/test/unit/create-user-ui-config-api-test.js new file mode 100644 index 0000000000..054349c72e --- /dev/null +++ b/test/unit/create-user-ui-config-api-test.js @@ -0,0 +1,39 @@ +import { afterEach, describe, test } from 'mocha' +import createUIConfigApi, { + __RewireAPI__ as createUIConfigApiRewireApi, +} from '../../lib/create-ui-config-api' +import { wrapUIConfig } from '../../lib/entities/ui-config' +import { cloneMock } from './mocks/entities' +import setupMakeRequest from './mocks/makeRequest' +import { entityUpdateTest, failingVersionActionTest } from './test-creators/instance-entity-methods' + +function setup(promise) { + const makeRequest = setupMakeRequest(promise) + const userUIConfigMock = cloneMock('userUIConfig') + const api = createUIConfigApi(makeRequest) + api.toPlainObject = () => userUIConfigMock + return { + api, + makeRequest, + entityMock: userUIConfigMock, + } +} + +describe('createUserUIConfigApi', () => { + afterEach(() => { + createUIConfigApiRewireApi.__ResetDependency__('entities') + }) + + test('UserUIConfig update', async () => { + return entityUpdateTest(setup, { + wrapperMethod: wrapUIConfig, + }) + }) + + test('UserUIConfig update fails', async () => { + return failingVersionActionTest(setup, { + wrapperMethod: wrapUIConfig, + actionMethod: 'update', + }) + }) +}) diff --git a/test/unit/entities/ui-config.js b/test/unit/entities/ui-config.js new file mode 100644 index 0000000000..d7ec222f23 --- /dev/null +++ b/test/unit/entities/ui-config.js @@ -0,0 +1,20 @@ +import { describe, test } from 'mocha' +import { wrapUIConfig } from '../../../lib/entities/ui-config' +import { cloneMock } from '../mocks/entities' +import setupMakeRequest from '../mocks/makeRequest' +import { entityWrappedTest } from '../test-creators/instance-entity-methods' + +function setup(promise) { + return { + makeRequest: setupMakeRequest(promise), + entityMock: cloneMock('uiConfig'), + } +} + +describe('Entity UIConfig', () => { + test('UIConfig is wrapped', async () => { + return entityWrappedTest(setup, { + wrapperMethod: wrapUIConfig, + }) + }) +}) diff --git a/test/unit/entities/user-ui-config.js b/test/unit/entities/user-ui-config.js new file mode 100644 index 0000000000..b029ea1035 --- /dev/null +++ b/test/unit/entities/user-ui-config.js @@ -0,0 +1,20 @@ +import { describe, test } from 'mocha' +import { wrapUserUIConfig } from '../../../lib/entities/user-ui-config' +import { cloneMock } from '../mocks/entities' +import setupMakeRequest from '../mocks/makeRequest' +import { entityWrappedTest } from '../test-creators/instance-entity-methods' + +function setup(promise) { + return { + makeRequest: setupMakeRequest(promise), + entityMock: cloneMock('userUIConfig'), + } +} + +describe('Entity UserUIConfig', () => { + test('UserUIConfig is wrapped', async () => { + return entityWrappedTest(setup, { + wrapperMethod: wrapUserUIConfig, + }) + }) +}) diff --git a/test/unit/mocks/entities.js b/test/unit/mocks/entities.js index 3b3a773513..36aa8e4d9d 100644 --- a/test/unit/mocks/entities.js +++ b/test/unit/mocks/entities.js @@ -777,6 +777,34 @@ export const workflowsChangelogEntryMock = { stepName: 'In review', } +export const uiConfigMock = { + sys: Object.assign(cloneDeep(sysMock), { + type: 'UIConfig', + space: { + sys: { id: 'space-id' }, + }, + environment: { + sys: { id: 'environment-id' }, + }, + }), + assetListViews: [], + entryListViews: [], +} + +export const userUIConfigMock = { + sys: Object.assign(cloneDeep(sysMock), { + type: 'UserUIConfig', + space: { + sys: { id: 'space-id' }, + }, + environment: { + sys: { id: 'environment-id' }, + }, + }), + assetListViews: [], + entryListViews: [], +} + const mocks = { apiKey: apiKeyMock, appAction: appActionMock, @@ -828,7 +856,9 @@ const mocks = { teamSpaceMembership: teamSpaceMembershipMock, upload: uploadMock, usage: usageMock, + uiConfig: uiConfigMock, user: userMock, + userUIConfig: userUIConfigMock, webhook: webhookMock, workflowStep: workflowStepMock, workflowDefinition: workflowDefinitionMock,