UNPKG

@inweb/client

Version:

JavaScript REST API client for the Open Cloud Server

544 lines (483 loc) 17.8 kB
/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2026, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a // license agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2026 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// import { IHttpClient } from "./IHttpClient"; import { Endpoint } from "./Endpoint"; import { IShortUserDesc } from "./IUser"; import { Role } from "./Role"; import { IRoleActions } from "./IRole"; import { Member } from "./Member"; import { File } from "./File"; import { normalizeParam, userFullName, userInitials } from "./Utils"; /** * Provides properties and methods for obtaining information about a project on the Open Cloud Server and * managing its {@link Role | roles}, {@link Member | members} and models. */ export class Project extends Endpoint { private _data: any; /** * @param data - Raw project data received from the server. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. * @param httpClient - HTTP client instance used to send requests to the REST API server. */ constructor(data: any, httpClient: IHttpClient) { super(`/projects/${data.id}`, httpClient); this.data = data; } /** * Project features the user has access to. * * @readonly */ get authorization(): { /** * Actions are allowed to be performed: * * - `update` - The ability to update the project details. * - `createTopic` - The ability to create a new topic. * - `createDocument` - The ability to create a new document. */ project_actions: string[]; } { return this.data.authorization; } /** * Project creation time (UTC) in the format specified in * {@link https://www.wikipedia.org/wiki/ISO_8601 | ISO 8601}. * * @readonly */ get createdAt(): string { return this.data.createdAt; } /** * Project custom fields object, to store custom data. */ get customFields(): any { return this.data.customFields; } set customFields(value: any) { this.data.customFields = value; } /** * Raw project data received from the server. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. * * @readonly */ get data(): any { return this._data; } private set data(value: any) { this._data = value; this._data.previewUrl = value.avatarUrl ? `${this.httpClient.serverUrl}/projects/${this._data.id}/preview?updated=${value.updatedAt}` : ""; this._data.owner.avatarUrl = `${this.httpClient.serverUrl}/users/${this._data.owner.userId}/avatar`; this._data.owner.fullName = userFullName(this._data.owner); this._data.owner.initials = userInitials(this._data.owner.fullName); } /** * Project description. */ get description(): string { return this.data.description; } set description(value: string) { this.data.description = value; } /** * Project end date in the format specified in * {@link https://www.wikipedia.org/wiki/ISO_8601 | ISO 8601}. */ get endDate(): string { return this.data.endDate; } set endDate(value: string | Date) { this.data.endDate = value instanceof Date ? value.toISOString() : value; } /** * Unique project ID. * * @readonly */ get id(): string { return this.data.id; } /** * The number of members in the project. * * @readonly */ get memberCount(): number { return this.data.memberCount; } /** * The number of models in the project. * * @readonly */ get modelCount(): number { return this.data.modelCount; } /** * Project name. */ get name(): string { return this.data.name; } set name(value: string) { this.data.name = value; } /** * Project owner (the user who created the project). * * @readonly */ get owner(): IShortUserDesc { return this.data.owner; } /** * Project preview image URL or empty string if the project does not have a preview. Use * {@link Project.setPreview | setPreview()} to change preview image. * * @readonly */ get previewUrl(): string { return this._data.previewUrl; } /** * `true` if project is shared project. */ get public(): boolean { return this.data.public; } set public(value: boolean) { this.data.public = value; } /** * Project start date in the format specified in * {@link https://www.wikipedia.org/wiki/ISO_8601 | ISO 8601}. */ get startDate(): string { return this.data.startDate; } set startDate(value: string | Date) { this.data.startDate = value instanceof Date ? value.toISOString() : value; } /** * The number of topics in the project. * * @readonly */ get topicCount(): number { return this.data.topicCount; } /** * Project last update time (UTC) in the format specified in * {@link https://www.wikipedia.org/wiki/ISO_8601 | ISO 8601}. * * @readonly */ get updatedAt(): string { return this.data.updatedAt; } /** * Reloads project data from the server. */ async checkout(): Promise<this> { const response = await this.get(""); this.data = await response.json(); return this; } /** * Updates project data on the server. * * @param data - Raw project data. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. */ async update(data: any): Promise<this> { const response = await this.put("", data); this.data = await response.json(); return this; } /** * Deletes a project from the server. * * @returns Returns the raw data of a deleted project. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. */ override delete(): Promise<any> { return super .delete("") .then((response) => response.text()) .then((text) => { // TODO fix for server 23.5 and below try { return JSON.parse(text); } catch { return { id: this.id }; } }); } /** * Saves project properties changes to the server. Call this method to update project data on the * server after any property changes. */ save(): Promise<this> { return this.update(this.data); } /** * Sets or removes the project preview. * * @param image - Preview image. Can be a * {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string, * {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer | ArrayBuffer}, * {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob} or * {@link https://developer.mozilla.org/docs/Web/API/File | Web API File} object. Setting the `image` * to `null` will remove the preview. */ async setPreview(image?: BodyInit | null): Promise<this> { if (!image) { await this.deletePreview(); } else { const response = await this.post("/preview", image); this.data = await response.json(); } return this; } /** * Removes the project preview. */ async deletePreview(): Promise<this> { const response = await super.delete("/preview"); this.data = await response.json(); return this; } /** * Returns a list of project roles. Project members have different abilities depending on the role they * have in a project. */ getRoles(): Promise<Role[]> { return this.get("/roles") .then((response) => response.json()) .then((array) => array.map((data) => new Role(data, this.id, this.httpClient))); } /** * Returns information about the specified project role. * * @param name - Role name. */ getRole(name: string): Promise<Role> { return this.get(`/roles/${name}`) .then((response) => response.json()) .then((data) => new Role(data, this.id, this.httpClient)); } /** * Creates a new project role. * * @param name - Role name. * @param description - Role description. * @param permissions - Actions are allowed to be performed for the role. */ createRole(name: string, description: string, permissions: IRoleActions): Promise<Role> { return this.post("/roles", { name, description, permissions: permissions || {}, }) .then((response) => response.json()) .then((data) => new Role(data, this.id, this.httpClient)); } /** * Deletes the specified project role. * * @param name - Role name. * @returns Returns the raw data of a deleted role. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. */ deleteRole(name: string): Promise<any> { return super.delete(`/roles/${name}`).then((response) => response.json()); } /** * Returns a list of project members. */ getMembers(): Promise<Member[]> { return this.get("/members") .then((response) => response.json()) .then((array) => array.map((data) => new Member(data, this.id, this.httpClient))); } /** * Returns information about the specified project member. * * @param memberId - Member ID. */ getMember(memberId: string): Promise<Member> { return this.get(`/members/${memberId}`) .then((response) => response.json()) .then((data) => new Member(data, this.id, this.httpClient)); } /** * Adds a user to the project to become a member and have permission to perform actions. * * @param userId - User ID. * @param role - Role name from the list of project {@link getRoles | roles}. */ addMember(userId: string, role: string): Promise<Member> { return this.post("/members", { userId, role }) .then((response) => response.json()) .then((data) => new Member(data, this.id, this.httpClient)); } /** * Removes the specified member from a project. * * @param memberId - Member ID. * @returns Returns the raw data of a deleted member. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Project | Open Cloud Projects API}. */ removeMember(memberId: string): Promise<any> { return super.delete(`/members/${memberId}`).then((response) => response.json()); } /** * Information about the file (model) that can be reference in the project topics. * * @typedef {any} FileInformation * @property {any[]} display_information - The list of fields to allow users to associate the file with * a server model. * @property {string} display_information.field_display_name - Field display name. * @property {string} display_information.field_value - Field value. * @property {any} file - The file reference object. * @property {string} file.file_name - File name. * @property {string} file.reference - File ID. */ /** * Returns a list of project files. For more information, see * {@link https://cloud.opendesign.com/docs//pages/server/bcf3.html#ProjectFilesInformation | Open Cloud BCF3 API}. * * This list contains all files that the project has access to. To add a file to this list, create a * {@link IGrantedTo.project | project} permission on the file using * {@link File.createPermission | File.createPermission()}. */ getFilesInformation(): Promise<any[]> { const bcfProjects = new Endpoint("/bcf/3.0/projects", this.httpClient, this.headers); return bcfProjects .get(`/${this.id}/files_information`) .then((response) => response.json()) .then((items) => { items.forEach((item) => { const getFieldValue = (displayName: string) => { return (item.display_information.find((x) => x.field_display_name === displayName) || {}).field_value; }; const previewUrl = `${this.httpClient.serverUrl}/files/${item.file.reference}/preview`; const ownerAvatarUrl = `${this.httpClient.serverUrl}/users/${getFieldValue("Owner")}/avatar`; const ownerFirstName = getFieldValue("Owner First Name"); const ownerLastName = getFieldValue("Owner Last Name"); const ownerUserName = getFieldValue("Owner User Name"); const ownerFullName = userFullName(ownerFirstName, ownerLastName, ownerUserName); const ownerInitials = userInitials(ownerFullName); item.display_information.push({ field_display_name: "Preview URL", field_value: previewUrl }); item.display_information.push({ field_display_name: "Owner Avatar URL", field_value: ownerAvatarUrl }); item.display_information.push({ field_display_name: "Owner Full Name", field_value: ownerFullName }); item.display_information.push({ field_display_name: "Owner Initials", field_value: ownerInitials }); // updatedBy since 24.10 const updatedByAvatarUrl = `${this.httpClient.serverUrl}/users/${getFieldValue("Updated By")}/avatar`; const updatedByFirstName = getFieldValue("Updated By First Name"); const updatedByLastName = getFieldValue("Updated By Last Name"); const updatedByUserName = getFieldValue("Updated By User Name"); const updatedByFullName = userFullName(updatedByFirstName, updatedByLastName, updatedByUserName); const updatedByInitials = userInitials(updatedByFullName); item.display_information.push({ field_display_name: "Updated By Avatar URL", field_value: updatedByAvatarUrl, }); item.display_information.push({ field_display_name: "Updated By Full Name", field_value: updatedByFullName }); item.display_information.push({ field_display_name: "Updated By Initials", field_value: updatedByInitials }); // geometryType since 24.12 const geometry = getFieldValue("Geometry Status"); const geometryGltf = getFieldValue("GeometryGltf Status"); const geometryType = geometry === "done" ? "vsfx" : geometryGltf === "done" ? "gltf" : ""; item.display_information.push({ field_display_name: "Geometry Type", field_value: geometryType }); }); return items; }); } /** * Returns a list of project files. */ getModels(): Promise<File[]> { return this.getFilesInformation() .then((filesInformation) => filesInformation.map((item) => item.file.reference)) .then((ids) => { const files = new Endpoint("/files", this.httpClient, this.headers); return files.get(`?id=${normalizeParam(ids)}`); }) .then((response) => response.json()) .then((files) => files.result.map((data) => new File(data, this.httpClient))); } /** * Adds a file to the project with specified permissions. * * To change file permissions for the project use {@link Permission.actions}. * * @param fileId - File ID. * @param actions - Actions are allowed to be performed on a file: * * - `read` - The ability to read file description, geometry data and properties. * - `readSourceFile` - The ability to download source file. * - `write` - The ability to modify file name, description and references. * - `readViewpoint` - The ability to read file viewpoints. * - `createViewpoint` - The ability to create file viewpoints. * - `deleteViewpoint` - The ability to delete file viewpoints. * * @returns Returns a file instance added to the project. */ async addModel(fileId: string, actions: string | string[]): Promise<File> { const files = new Endpoint("/files", this.httpClient, this.headers); const file = await files .get(`/${fileId}`) .then((response) => response.json()) .then((data) => new File(data, this.httpClient)); const grantedTo = [{ project: { id: this.id, name: this.name } }]; await file.createPermission(actions, grantedTo); return file; } /** * Removes the specified file from a project. * * @param fileId - File ID. * @returns Returns a file instance removed from the project. */ async removeModel(fileId: string): Promise<File> { const files = new Endpoint("/files", this.httpClient, this.headers); const file = await files .get(`/${fileId}`) .then((response) => response.json()) .then((data) => new File(data, this.httpClient)); const permissions = await file.getPermissions(); await Promise.allSettled( permissions .filter((permission) => permission.grantedTo.some((x) => x.project?.id === this.id)) .map((permission) => permission.delete()) ); return file; } }