Skip to content

Commit

Permalink
feat(workspace): add CRUD operations for projects and resources (#744)
Browse files Browse the repository at this point in the history
  • Loading branch information
agong-coveo authored Sep 27, 2023
1 parent 8c7dad0 commit 3a71f44
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/resources/PlatformResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import Vaults from './Vaults/Vaults.js';
import TableauService from './TableauService/TableauService.js';
import HostedPages from './HostedPages/HostedPages.js';
import SearchAnalysis from './SearchAnalysis/SearchAnalysis.js';
import Project from './Projects/Project.js';
import Resources from './Resources/Resources.js';

const resourcesMap: Array<{key: string; resource: typeof Resource}> = [
{key: 'activity', resource: Activity},
Expand Down Expand Up @@ -102,6 +104,8 @@ const resourcesMap: Array<{key: string; resource: typeof Resource}> = [
{key: 'privilegeEvaluator', resource: PrivilegeEvaluator},
{key: 'tableauService', resource: TableauService},
{key: 'searchAnalysis', resource: SearchAnalysis},
{key: 'project', resource: Project},
{key: 'resources', resource: Resources},
];

class PlatformResources {
Expand Down Expand Up @@ -156,6 +160,8 @@ class PlatformResources {
user: User;
vault: Vaults;
tableauService: TableauService;
project: Project;
resources: Resources;

registerAll() {
resourcesMap.forEach(({key, resource}) => {
Expand Down
77 changes: 77 additions & 0 deletions src/resources/Projects/Project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import API from '../../APICore.js';
import {PageModel} from '../BaseInterfaces.js';
import Resource from '../Resource.js';
import {ResourceModel, ResourceParams} from '../Resources/index.js';
import {ListProjectParams, ProjectModel, BaseProjectModel, ProjectResourceType} from './ProjectInterfaces.js';

export default class Project extends Resource {
static baseUrl = `/rest/organizations/${API.orgPlaceholder}/projects`;

/**
* Returns a paginated list of projects.
*
* @param {ListProjectParams} params
* @returns {Promise<PageModel<ProjectModel>>} A paginated list of projects
*/
list(params?: ListProjectParams): Promise<PageModel<ProjectModel>> {
return this.api.get<PageModel<ProjectModel>>(this.buildPath(Project.baseUrl, params));
}

/**
* Creates and returns a new project.
*
* @param {ProjectModel} project
* @returns {Promise<ProjectModel>} The newly created project
*/
create(project: BaseProjectModel): Promise<ProjectModel> {
return this.api.post<ProjectModel>(this.buildPath(Project.baseUrl), project);
}

/**
* Updates a project with the model sent and returns the updated project.
*
* @param {string} projectId
* @param {ProjectModel} updateProjectModel
* @returns {Promise<ProjectModel>} The updated project
*/
update(projectId: string, updateProjectModel: ProjectModel): Promise<ProjectModel> {
return this.api.put<ProjectModel>(this.buildPath(`${Project.baseUrl}/${projectId}`), updateProjectModel);
}

/**
* Returns a project.
*
* @param {string} projectId
* @returns {Promise<ProjectModel>} The project specified by the provided id
*/
get(projectId: string): Promise<ProjectModel> {
return this.api.get<ProjectModel>(this.buildPath(`${Project.baseUrl}/${projectId}`));
}

/**
* Deletes a project.
*
* @param {string} projectId
*/
delete(projectId: string): Promise<void> {
return this.api.delete(this.buildPath(`${Project.baseUrl}/${projectId}`));
}

/**
* Returns a paginated list of resources associated to a project.
*
* @param projectId
* @param resourceType
* @param {ResourceParams} params
* @returns {Promise<PageModel<ResourceModel>>} A paginated list of resources associated to a project.
*/
listResources(
projectId: string,
resourceType: ProjectResourceType,
params?: ResourceParams,
): Promise<PageModel<ResourceModel>> {
return this.api.get<PageModel<ResourceModel>>(
this.buildPath(`${Project.baseUrl}/${projectId}/resources/${resourceType}`, params),
);
}
}
121 changes: 121 additions & 0 deletions src/resources/Projects/ProjectInterfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {Paginated} from '../BaseInterfaces.js';
import {SortingOrder} from '../Enums.js';

export enum ProjectSortBy {
name = 'NAME',
createdBy = 'CREATED_BY',
createdDate = 'CREATED_DATE',
updatedBy = 'UPDATED_BY',
updatedDate = 'UPDATED_DATE',
}

export const projectResourceTypes = [
'CATALOG',
'CASE_ASSIST',
'CRAWLING_MODULE',
'EXTENSION',
'IN_PRODUCT_EXPERIENCE',
'INSIGHT_PANEL',
'ML_MODEL',
'QUERY_PIPELINE',
'SEARCH_HUB',
'SEARCH_PAGE',
'SECURITY_PROVIDER',
'SOURCE',
'UA_REPORT',
];

export type ProjectResourceType = (typeof projectResourceTypes)[number];

export enum SolutionType {
Commerce = 'COMMERCE',
Other = 'OTHER',
Service = 'SERVICE',
Website = 'WEBSITE',
Workplace = 'WORKPLACE',
}

export interface BaseProjectModel {
/**
* The name of the project.
*/
name: string;
/**
* The description of the project.
*/
description: string;
/**
* The solution type of the project.
*/
solutionType: SolutionType;
/**
* The list of usernames that will be points of contact for the project.
*
* @example: ['[email protected]', '[email protected]']
*/
pointsOfContact?: string[];
/**
* The resources associated to the project.
*
* @example: {'SOURCE': ['sourceId1', 'sourceId2']}
*/
resources?: Record<ProjectResourceType, string[]>;
}

export interface ProjectModel extends BaseProjectModel {
/**
* The unique identitifier of the project.
*/
id: string;
/**
* The email of the user that created the project.
*
* @example: '[email protected]'
*/
createdBy: string;
/**
* The date of the project's creation.
* Note: ISO-8601 format
*
* @example: '2023-06-21T14:59:26.850Z'
*/
createdDate: string;
/**
* The email of the user that last updated the project.
*
* @example: '[email protected]'
*/
updatedBy: string;
/**
* The date of the project's last update.
* Note: ISO-8601 format
*
* @example: '2023-06-21T14:59:26.850Z'
*/
updatedDate: string;
}

export interface ListProjectParams extends Paginated {
/**
* The query filter to match.
* This allows you to search according to the project name.
*
* By default, results are not required to match a specific query filter.
*/
filter?: string;
/**
* The sorting criteria to apply on the results.
*
*/
sortBy?: ProjectSortBy;
/**
* The sorting order to apply on the results.
*
* @example: 'ASC'
*/
order?: SortingOrder;
/**
* Whether to include resources when returning results.
*/
includeResources?: boolean;
}
2 changes: 2 additions & 0 deletions src/resources/Projects/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Project.js';
export * from './ProjectInterfaces.js';
115 changes: 115 additions & 0 deletions src/resources/Projects/tests/Project.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import API from '../../../APICore.js';
import Project from '../Project.js';
import {BaseProjectModel, ProjectModel, SolutionType} from '../ProjectInterfaces.js';

jest.mock('../../../APICore.js');

const APIMock: jest.Mock<API> = API as any;

describe('Project', () => {
let project: Project;
const api = new APIMock() as jest.Mocked<API>;
const serverlessApi = new APIMock() as jest.Mocked<API>;
const mockProjectId = 'randomProjectId';
const mockRandomResourceType = 'CATALOG';

const mockNewProject: BaseProjectModel = {
name: 'Pokemon Project',
description: 'Project about pokemons',
solutionType: SolutionType.Commerce,
};

const mockProject: ProjectModel = {
id: mockProjectId,
name: 'Pokemon Project',
description: 'Project about pokemons',
solutionType: SolutionType.Commerce,
createdBy: '[email protected]',
updatedBy: '[email protected]',
createdDate: '2023-07-19T02:37:23.399Z',
updatedDate: '2023-07-22T21:52:22.588Z',
};

beforeEach(() => {
project = new Project(api, serverlessApi);

jest.resetAllMocks();
});

describe('list', () => {
it('should make a GET call to the correct Project url', () => {
project.list();
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(Project.baseUrl);
});

it('should use the passed parameters as its query paramaters', () => {
project.list({page: 1, perPage: 50});
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(`${Project.baseUrl}?page=1&perPage=50`);
});
});

describe('create', () => {
it('should make a POST call to the correct Project url', () => {
project.create(mockNewProject);
expect(api.post).toHaveBeenCalledTimes(1);
expect(api.post).toHaveBeenCalledWith(Project.baseUrl, {
name: 'Pokemon Project',
description: 'Project about pokemons',
solutionType: SolutionType.Commerce,
});
});
});

describe('update', () => {
it('should make a PUT call to the correct Project url', () => {
project.update(mockProjectId, mockProject);
expect(api.put).toHaveBeenCalledTimes(1);
expect(api.put).toHaveBeenCalledWith(`${Project.baseUrl}/randomProjectId`, {
id: mockProjectId,
name: 'Pokemon Project',
description: 'Project about pokemons',
solutionType: SolutionType.Commerce,
createdBy: '[email protected]',
updatedBy: '[email protected]',
createdDate: '2023-07-19T02:37:23.399Z',
updatedDate: '2023-07-22T21:52:22.588Z',
});
});
});

describe('get', () => {
it('should make a GET call to the correct Project URL', () => {
project.get(mockProjectId);
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(`${Project.baseUrl}/randomProjectId`);
});
});

describe('delete', () => {
it('should make a DELETE call to the correct Project URL', () => {
project.delete(mockProjectId);
expect(api.delete).toHaveBeenCalledTimes(1);
expect(api.delete).toHaveBeenCalledWith(`${Project.baseUrl}/randomProjectId`);
});
});

describe('listResources', () => {
it('should make a GET call to the correct Project URL', () => {
project.listResources(mockProjectId, mockRandomResourceType);
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(
`${Project.baseUrl}/${mockProjectId}/resources/${mockRandomResourceType}`,
);
});

it('should use the passed parameters as its query paramaters', () => {
project.listResources(mockProjectId, mockRandomResourceType, {page: 1, perPage: 50});
expect(api.get).toHaveBeenCalledTimes(1);
expect(api.get).toHaveBeenCalledWith(
`${Project.baseUrl}/${mockProjectId}/resources/${mockRandomResourceType}?page=1&perPage=50`,
);
});
});
});
20 changes: 20 additions & 0 deletions src/resources/Resources/Resources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import API from '../../APICore.js';
import {PageModel} from '../BaseInterfaces.js';
import {ProjectResourceType} from '../Projects/ProjectInterfaces.js';
import Resource from '../Resource.js';
import {ResourceParams, ResourceModel} from './ResourcesInterfaces.js';

export default class Resources extends Resource {
static baseUrl = `/rest/organizations/${API.orgPlaceholder}/resources`;

/**
* Returns a paginated list of resources.
*
* @param resourceType
* @param {ListProjectParams} params
* @returns {Promise<PageModel<ResourceModel>>} A paginated list of resources.
*/
list(resourceType: ProjectResourceType, params?: ResourceParams): Promise<PageModel<ResourceModel>> {
return this.api.get<PageModel<ResourceModel>>(this.buildPath(`${Resources.baseUrl}/${resourceType}`, params));
}
}
27 changes: 27 additions & 0 deletions src/resources/Resources/ResourcesInterfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Paginated} from '../BaseInterfaces.js';

export interface ResourceModel {
/**
* The unique identifier of a resource within a project
*/
id: string;
/**
* The name of the resouce
*/
name: string;
/**
* The type of the resource
*/
type?: string;
/**
* The version of the resource
*/
version?: string;
}

export interface ResourceParams extends Paginated {
/**
* Term to filter resources by id or name
*/
filter?: string;
}
2 changes: 2 additions & 0 deletions src/resources/Resources/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Resources.js';
export * from './ResourcesInterfaces.js';
Loading

0 comments on commit 3a71f44

Please sign in to comment.