Skip to content

Commit dd4e663

Browse files
feat: add resource methods [DANTE-1405] (#2436)
* feat: add Resource Type to contentful-management [DANTE-1832] * feat: add tests * feat: add getMany and orgId to ResourceType APIs * fix: unit test * fix: remove resource type id in test * feat: update comments, add more tests * feat: add resource methods * fix: unit tests * fix: lint * fix: improve the mocked data in functions for a better integration test * feat: export the resource related types * fix: wait 1s after deletion * fix: remove type id from getMany * fix: make the query params optional for resources and resourceTypes --------- Co-authored-by: Maya <[email protected]>
1 parent 2b67208 commit dd4e663

21 files changed

+721
-10
lines changed

lib/adapters/REST/endpoints/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import * as AccessToken from './access-token'
3535
import * as PreviewApiKey from './preview-api-key'
3636
import * as Release from './release'
3737
import * as ReleaseAction from './release-action'
38+
import * as Resource from './resource'
3839
import * as ResourceProvider from './resource-provider'
3940
import * as ResourceType from './resource-type'
4041
import * as Role from './role'
@@ -96,6 +97,7 @@ export default {
9697
PreviewApiKey,
9798
Release,
9899
ReleaseAction,
100+
Resource,
99101
ResourceProvider,
100102
ResourceType,
101103
Role,

lib/adapters/REST/endpoints/resource-type.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ import type { RawAxiosRequestHeaders } from 'axios'
22
import type { AxiosInstance } from 'contentful-sdk-core'
33
import * as raw from './raw'
44
import copy from 'fast-copy'
5-
import type { CollectionProp } from '../../../common-types'
6-
import { type GetResourceTypeParams } from '../../../common-types'
5+
import type {
6+
BasicCursorPaginationOptions,
7+
CursorPaginatedCollectionProp,
8+
GetResourceTypeParams,
9+
CollectionProp,
10+
GetSpaceEnvironmentParams,
11+
} from '../../../common-types'
712
import type { RestEndpoint } from '../types'
8-
import type { ResourceTypeProps, UpsertResourceTypeProps } from '../../../entities/resource-type'
13+
import type {
14+
ResourceTypeProps,
15+
SpaceEnvResourceTypeProps,
16+
UpsertResourceTypeProps,
17+
} from '../../../entities/resource-type'
918

1019
const getBaseUrl = (
1120
params: GetResourceTypeParams | Omit<GetResourceTypeParams, 'resourceTypeId'>
@@ -15,6 +24,10 @@ const getBaseUrl = (
1524
const getEntityUrl = (params: GetResourceTypeParams) =>
1625
`${getBaseUrl(params)}/${params.resourceTypeId}`
1726

27+
const getSpaceEnvUrl = (
28+
params: GetSpaceEnvironmentParams & { query?: BasicCursorPaginationOptions }
29+
) => `/spaces/${params.spaceId}/environments/${params.environmentId}/resource_types`
30+
1831
export const get: RestEndpoint<'ResourceType', 'get'> = (
1932
http: AxiosInstance,
2033
params: GetResourceTypeParams
@@ -46,3 +59,13 @@ export const getMany: RestEndpoint<'ResourceType', 'getMany'> = (
4659
) => {
4760
return raw.get<CollectionProp<ResourceTypeProps>>(http, getBaseUrl(params))
4861
}
62+
63+
export const getForEnvironment: RestEndpoint<'ResourceType', 'getForEnvironment'> = (
64+
http: AxiosInstance,
65+
params: GetSpaceEnvironmentParams & { query?: BasicCursorPaginationOptions }
66+
) => {
67+
return raw.get<CursorPaginatedCollectionProp<SpaceEnvResourceTypeProps>>(
68+
http,
69+
getSpaceEnvUrl(params)
70+
)
71+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { CursorPaginatedCollectionProp, GetResourceParams } from '../../../common-types'
2+
import type { RestEndpoint } from '../types'
3+
import type { AxiosInstance } from 'contentful-sdk-core'
4+
import * as raw from './raw'
5+
import type { ResourceProps, ResourceQueryOptions } from '../../../entities/resource'
6+
7+
const getBaseUrl = (params: GetResourceParams) =>
8+
`/spaces/${params.spaceId}/environments/${params.environmentId}/resource_types/${params.resourceTypeId}/resources`
9+
10+
export const getMany: RestEndpoint<'Resource', 'getMany'> = (
11+
http: AxiosInstance,
12+
params: GetResourceParams & { query?: ResourceQueryOptions }
13+
) =>
14+
raw.get<CursorPaginatedCollectionProp<ResourceProps>>(http, getBaseUrl(params), {
15+
params: params.query,
16+
})

lib/common-types.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ import type {
165165
ResourceProviderProps,
166166
UpsertResourceProviderProps,
167167
} from './entities/resource-provider'
168-
import type { ResourceTypeProps, UpsertResourceTypeProps } from './entities/resource-type'
168+
import type {
169+
ResourceTypeProps,
170+
SpaceEnvResourceTypeProps,
171+
UpsertResourceTypeProps,
172+
} from './entities/resource-type'
173+
import type { ResourceProps, ResourceQueryOptions } from './entities/resource'
169174

170175
export interface DefaultElements<TPlainObject extends object = object> {
171176
toPlainObject(): TPlainObject
@@ -594,14 +599,18 @@ type MRInternal<UA extends boolean> = {
594599
'ReleaseAction',
595600
'queryForRelease'
596601
>
597-
602+
(opts: MROpts<'Resource', 'getMany', UA>): MRReturn<'Resource', 'getMany'>
598603
(opts: MROpts<'ResourceProvider', 'get', UA>): MRReturn<'ResourceProvider', 'get'>
599604
(opts: MROpts<'ResourceProvider', 'upsert', UA>): MRReturn<'ResourceProvider', 'upsert'>
600605
(opts: MROpts<'ResourceProvider', 'delete', UA>): MRReturn<'ResourceProvider', 'delete'>
601606

602607
(opts: MROpts<'ResourceType', 'get', UA>): MRReturn<'ResourceType', 'get'>
603608
(opts: MROpts<'ResourceType', 'upsert', UA>): MRReturn<'ResourceType', 'upsert'>
604609
(opts: MROpts<'ResourceType', 'delete', UA>): MRReturn<'ResourceType', 'delete'>
610+
(opts: MROpts<'ResourceType', 'getForEnvironment', UA>): MRReturn<
611+
'ResourceType',
612+
'getForEnvironment'
613+
>
605614
(opts: MROpts<'ResourceType', 'getMany', UA>): MRReturn<'ResourceType', 'getMany'>
606615

607616
(opts: MROpts<'Role', 'get', UA>): MRReturn<'Role', 'get'>
@@ -776,6 +785,13 @@ export interface Adapter {
776785
* @private
777786
*/
778787
export type MRActions = {
788+
Resource: {
789+
getMany: {
790+
params: GetResourceParams & { query?: ResourceQueryOptions }
791+
headers?: RawAxiosRequestHeaders
792+
return: CursorPaginatedCollectionProp<ResourceProps>
793+
}
794+
}
779795
ResourceProvider: {
780796
get: { params: GetResourceProviderParams; return: ResourceProviderProps }
781797
upsert: {
@@ -799,6 +815,10 @@ export type MRActions = {
799815
return: ResourceTypeProps
800816
}
801817
delete: { params: GetResourceTypeParams; return: any }
818+
getForEnvironment: {
819+
params: GetSpaceEnvironmentParams & { query?: BasicCursorPaginationOptions }
820+
return: CursorPaginatedCollectionProp<SpaceEnvResourceTypeProps>
821+
}
802822
}
803823
Http: {
804824
get: { params: { url: string; config?: RawAxiosRequestConfig }; return: any }
@@ -2118,6 +2138,8 @@ export type GetResourceProviderParams = GetOrganizationParams & { appDefinitionI
21182138

21192139
export type GetResourceTypeParams = GetResourceProviderParams & { resourceTypeId: string }
21202140

2141+
export type GetResourceParams = GetSpaceEnvironmentParams & { resourceTypeId: string }
2142+
21212143
export type QueryParams = { query?: QueryOptions }
21222144
export type SpaceQueryParams = { query?: SpaceQueryOptions }
21232145
export type PaginationQueryParams = { query?: PaginationQueryOptions }

lib/create-environment-api.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { wrapUIConfig } from './entities/ui-config'
4747
import { wrapUserUIConfig } from './entities/user-ui-config'
4848
import { wrapEnvironmentTemplateInstallationCollection } from './entities/environment-template-installation'
4949
import type { CreateAppAccessTokenProps } from './entities/app-access-token'
50+
import type { ResourceQueryOptions } from './entities/resource'
5051

5152
/**
5253
* @private
@@ -75,6 +76,8 @@ export default function createEnvironmentApi(makeRequest: MakeRequest) {
7576
const { wrapAppActionCall } = entities.appActionCall
7677
const { wrapBulkAction } = entities.bulkAction
7778
const { wrapAppAccessToken } = entities.appAccessToken
79+
const { wrapResourceTypesForEnvironmentCollection } = entities.resourceType
80+
const { wrapResourceCollection } = entities.resource
7881

7982
return {
8083
/**
@@ -2280,5 +2283,81 @@ export default function createEnvironmentApi(makeRequest: MakeRequest) {
22802283
},
22812284
}).then((data) => wrapEnvironmentTemplateInstallationCollection(makeRequest, data))
22822285
},
2286+
2287+
/**
2288+
* Gets a collection of all resource types based on native external references app installations in the environment
2289+
* @param query - BasicCursorPaginationOptions
2290+
* @return Promise for a collection of ResourceTypes
2291+
* ```javascript
2292+
* const contentful = require('contentful-management')
2293+
*
2294+
* const client = contentful.createClient({
2295+
* accessToken: '<content_management_api_key>'
2296+
* })
2297+
*
2298+
* client.getSpace('<space_id>')
2299+
* .then((space) => space.getEnvironment('<environment_id>'))
2300+
* .then((environment) => environment.getResourceTypes({limit: 10}))
2301+
* .then((installations) => console.log(installations.items))
2302+
* .catch(console.error)
2303+
* ```
2304+
*/
2305+
async getResourceTypes(query?: BasicCursorPaginationOptions) {
2306+
const raw: EnvironmentProps = this.toPlainObject()
2307+
2308+
return makeRequest({
2309+
entityType: 'ResourceType',
2310+
action: 'getForEnvironment',
2311+
params: {
2312+
query,
2313+
spaceId: raw.sys.space.sys.id,
2314+
environmentId: raw.sys.id,
2315+
},
2316+
}).then((data) => wrapResourceTypesForEnvironmentCollection(makeRequest, data))
2317+
},
2318+
2319+
/**
2320+
* Gets a collection of all resources for a given resource type based on native external references app installations in the environment
2321+
* @param resourceTypeId - Id of the resourceType to get its resources
2322+
* @param query - Either LookupQuery options with 'sys.urn[in]' param or a Search query with 'query' param, in both cases you can add pagination options
2323+
* @return Promise for a collection of Resources for a given resourceTypeId
2324+
* ```javascript
2325+
* const contentful = require('contentful-management')
2326+
*
2327+
* const client = contentful.createClient({
2328+
* accessToken: '<content_management_api_key>'
2329+
* })
2330+
*
2331+
* // Search Query
2332+
* client.getSpace('<space_id>')
2333+
* .then((space) => space.getEnvironment('<environment_id>'))
2334+
* // <search_query> is a string you want to search for in the external resources
2335+
* .then((environment) => environment.getResourcesForResourceType('<resource_type_id>', {query: '<search_query>', limit: 10}))
2336+
* .then((installations) => console.log(installations.items))
2337+
* .catch(console.error)
2338+
*
2339+
* // Lookup query
2340+
*
2341+
* client.getSpace('<space_id>')
2342+
* .then((space) => space.getEnvironment('<environment_id>'))
2343+
* .then((environment) => environment.getResourcesForResourceType('<resource_type_id>', {'sys.urn[in]': '<resource_urn1>,<resource_urn2>', limit: 10}))
2344+
* .then((installations) => console.log(installations.items))
2345+
* .catch(console.error)
2346+
* ```
2347+
*/
2348+
async getResourcesForResourceType(resourceTypeId: string, query?: ResourceQueryOptions) {
2349+
const raw: EnvironmentProps = this.toPlainObject()
2350+
2351+
return makeRequest({
2352+
entityType: 'Resource',
2353+
action: 'getMany',
2354+
params: {
2355+
query,
2356+
spaceId: raw.sys.space.sys.id,
2357+
environmentId: raw.sys.id,
2358+
resourceTypeId,
2359+
},
2360+
}).then((data) => wrapResourceCollection(makeRequest, data))
2361+
},
22832362
}
22842363
}

lib/entities/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import * as concept from './concept'
5454
import * as conceptScheme from './concept-scheme'
5555
import * as resourceProvider from './resource-provider'
5656
import * as resourceType from './resource-type'
57+
import * as resource from './resource'
5758

5859
export default {
5960
accessToken,
@@ -94,6 +95,7 @@ export default {
9495
releaseAction,
9596
resourceProvider,
9697
resourceType,
98+
resource,
9799
role,
98100
scheduledAction,
99101
snapshot,

lib/entities/resource-provider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ function createResourceProviderApi(makeRequest: MakeRequest) {
126126
appDefinitionId: this.sys.appDefinition.sys.id,
127127
resourceTypeId: id,
128128
},
129+
headers: {},
129130
payload: data,
130131
}).then((data) => wrapResourceType(makeRequest, data))
131132
},

lib/entities/resource-type.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
BasicMetaSysProps,
3+
CursorPaginatedCollectionProp,
34
DefaultElements,
45
GetResourceTypeParams,
56
MakeRequest,
@@ -8,15 +9,16 @@ import type {
89
import { toPlainObject, freezeSys } from 'contentful-sdk-core'
910
import copy from 'fast-copy'
1011
import enhanceWithMethods from '../enhance-with-methods'
12+
import { wrapCursorPaginatedCollection } from '../common-utils'
1113

1214
export type ResourceTypeProps = {
1315
/**
1416
* System metadata
1517
*/
1618
sys: Omit<BasicMetaSysProps, 'version'> & {
1719
appDefinition: SysLink
18-
organization: SysLink
1920
resourceProvider: SysLink
21+
organization: SysLink
2022
}
2123
/**
2224
* Resource Type name
@@ -40,6 +42,24 @@ export type ResourceTypeProps = {
4042
}
4143
}
4244
}
45+
const publicResourceTypeFields = ['name'] as const
46+
47+
type OptionalSysFields =
48+
| 'createdAt'
49+
| 'createdBy'
50+
| 'updatedAt'
51+
| 'updatedBy'
52+
| 'appDefinition'
53+
| 'organization'
54+
55+
export type SpaceEnvResourceTypeProps = Pick<
56+
ResourceTypeProps,
57+
typeof publicResourceTypeFields[number]
58+
> & {
59+
// we mark timestamps and users as optional to include system types like `Contentful:Entry` into the public response
60+
sys: Partial<Pick<ResourceTypeProps['sys'], OptionalSysFields>> &
61+
Omit<ResourceTypeProps['sys'], OptionalSysFields>
62+
}
4363

4464
export type UpsertResourceTypeProps = Omit<ResourceTypeProps, 'sys'>
4565

@@ -138,3 +158,18 @@ export function wrapResourceType(makeRequest: MakeRequest, data: ResourceTypePro
138158
)
139159
return freezeSys(ResourceTypeWithMethods)
140160
}
161+
162+
export function wrapResourceTypeforEnvironment(
163+
makeRequest: MakeRequest,
164+
data: SpaceEnvResourceTypeProps
165+
): SpaceEnvResourceTypeProps {
166+
const resourceType = toPlainObject(data)
167+
return freezeSys(resourceType)
168+
}
169+
170+
export const wrapResourceTypesForEnvironmentCollection: (
171+
makeRequest: MakeRequest,
172+
data: CursorPaginatedCollectionProp<SpaceEnvResourceTypeProps>
173+
) => CursorPaginatedCollectionProp<SpaceEnvResourceTypeProps> = wrapCursorPaginatedCollection(
174+
wrapResourceTypeforEnvironment
175+
)

lib/entities/resource.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type {
2+
BasicCursorPaginationOptions,
3+
CursorPaginatedCollectionProp,
4+
MakeRequest,
5+
SysLink,
6+
} from '../common-types'
7+
import { wrapCursorPaginatedCollection } from '../common-utils'
8+
import { freezeSys, toPlainObject } from 'contentful-sdk-core'
9+
10+
export type ResourceQueryOptions = LookupQueryOptions | SearchQueryOptions
11+
12+
type LookupQueryOptions = {
13+
'sys.urn[in]': string
14+
} & BasicCursorPaginationOptions
15+
16+
type SearchQueryOptions = {
17+
query: string
18+
} & BasicCursorPaginationOptions
19+
20+
export type ResourceProps = {
21+
sys: {
22+
type: 'Resource'
23+
urn: string
24+
resourceType: SysLink
25+
resourceProvider: SysLink
26+
appDefinition: SysLink
27+
}
28+
fields: {
29+
title: string
30+
subtitle?: string
31+
description?: string
32+
externalUrl?: string
33+
image?: {
34+
url: string
35+
altText?: string
36+
}
37+
badge?: {
38+
label: string
39+
variant: 'primary' | 'negative' | 'positive' | 'warning' | 'secondary'
40+
}
41+
}
42+
}
43+
export function wrapResource(makeRequest: MakeRequest, data: ResourceProps) {
44+
const resource = toPlainObject(data)
45+
return freezeSys(resource)
46+
}
47+
export const wrapResourceCollection: (
48+
makeRequest: MakeRequest,
49+
data: CursorPaginatedCollectionProp<ResourceProps>
50+
) => CursorPaginatedCollectionProp<ResourceProps> = wrapCursorPaginatedCollection(wrapResource)

lib/export-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,5 +290,7 @@ export type {
290290
export type {
291291
ResourceType,
292292
ResourceTypeProps,
293+
SpaceEnvResourceTypeProps,
293294
UpsertResourceTypeProps,
294295
} from './entities/resource-type'
296+
export type { ResourceProps, ResourceQueryOptions } from './entities/resource'

0 commit comments

Comments
 (0)