/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import type { V1_ContractUserStatusResponse, V1_CreateContractPayload, V1_CreateDataAccessRequestPayload, V1_CreateSubscriptionInput, V1_DataContractApprovedUsersResponse, V1_DataContractsResponse, V1_DataProductLitePaginatedResponse, V1_DataRequestsWithWorkflowResponse, V1_DataRequestTasksResponse, V1_DataSubscriptionResponse, V1_DataSubscriptionsPaginatedResponse, V1_EntitlementsDataProductDetailsResponse, V1_EntitlementsDataProductLiteResponse, V1_EntitlementsLakehouseEnvironmentType, V1_EntitlementsUserEnvResponse, V1_InvalidateDataContractResponse, V1_LastValuesMapRecord, V1_LiteDataContractsPaginatedResponse, V1_LiteDataContractsResponse, V1_LiteDataContractWithUserStatus, V1_PaginationMetadataRecord, V1_PendingTasksResponse, V1_TaskResponse, V1_TaskStatus, V1_UserPendingContractsRecord, V1_UserPendingContractsResponse, } from '@finos/legend-graph'; import { AbstractServerClient, type PlainObject } from '@finos/legend-shared'; import type { LakehouseConsumerGrantResponse } from './models/ConsumerGrants.js'; export interface LakehouseContractServerClientConfig { baseUrl: string; } export class LakehouseContractServerClient extends AbstractServerClient { constructor(config: LakehouseContractServerClientConfig) { super({ baseUrl: config.baseUrl, }); } // auth private _token = (token?: string) => ({ Authorization: `Bearer ${token}`, }); // ------------------------------------------- Data Contracts ------------------------------------------- private _dataContracts = (): string => `${this.baseUrl}/datacontracts`; getLiteDataContractsPaginated = ( size: number, lastContractId: string | undefined, token: string | undefined, ): Promise> => this.get( `${this._dataContracts()}/litePaginated?size=${size}${lastContractId ? `&lastContractId=${lastContractId}` : ''}`, {}, this._token(token), ); getDataContract = ( id: string, withMembers: boolean, token: string | undefined, ): Promise> => this.get( `${this._dataContracts()}/${encodeURIComponent(id)}?withMembers=${withMembers}`, {}, this._token(token), ); getApprovedUsersForDataContract = ( id: string, token: string | undefined, ): Promise> => this.get( `${this._dataContracts()}/${encodeURIComponent(id)}/approvedUsers`, {}, this._token(token), ); getContractUserStatus = ( contractId: string, userId: string, token: string | undefined, ): Promise> => this.get( `${this._dataContracts()}/${encodeURIComponent(contractId)}/user/${encodeURIComponent(userId)}`, {}, this._token(token), ); getDataContractsFromDID = ( body: PlainObject[], token: string | undefined, ): Promise> => { return this.post( `${this._dataContracts()}/query/accessPointsOwnedByDeployments`, body, undefined, this._token(token), ); }; getDataContractsForDataProduct = ( resourceType: string, resourceId: string, did: number, token: string | undefined, ): Promise> => { return this.get( `${this._dataContracts()}/lite/resourceType/${encodeURIComponent(resourceType)}/resourceId/${encodeURIComponent(resourceId)}/did/${encodeURIComponent(did)}`, {}, this._token(token), ); }; getContractsForUser = ( user: string, token: string | undefined, ): Promise[]> => { return this.get( `${this._dataContracts()}/user/${user}`, {}, this._token(token), ); }; getContractsCreatedByUser = ( user: string, token: string | undefined, ): Promise[]> => { return this.get( `${this._dataContracts()}/createdBy/${user}`, {}, this._token(token), ); }; getPendingContracts = ( user: string | undefined, token: string | undefined, ): Promise> => { return this.get( `${this._dataContracts()}/pendingContractsForUser`, {}, this._token(token), { user }, ); }; createContract = ( contractRequest: PlainObject, token: string | undefined, ): Promise => this.post( `${this._dataContracts()}`, contractRequest, undefined, this._token(token), ); escalateUserOnContract = ( contractId: string, user: string, forSystemAccount: boolean, token: string | undefined, ): Promise> => this.post( `${this._dataContracts()}/escalate/${encodeURIComponent(contractId)}/user/${encodeURIComponent(user)}?forSystemAccount=${forSystemAccount}`, {}, {}, this._token(token), ); invalidateContract = ( contractId: string, token: string | undefined, ): Promise> => this.delete( `${this._dataContracts()}/invalidate/${encodeURIComponent(contractId)}`, {}, {}, this._token(token), ); // ------------------------------------------- Tasks ------------------------------------------- private _tasks = (): string => `${this.baseUrl}/datacontracts/tasks`; private _contract_tasks = (): string => `${this._tasks()}/query/contract`; getPendingTasks = ( user: string | undefined, token: string | undefined, ): Promise> => { return this.get(`${this._tasks()}/pending`, {}, this._token(token), { user, }); }; getTask = ( taskId: string, token: string | undefined, ): Promise> => { return this.get( `${this._tasks()}/${encodeURIComponent(taskId)}`, {}, this._token(token), undefined, ); }; getContractTasks = ( contractId: string, token: string | undefined, ): Promise> => { return this.get( `${this._contract_tasks()}/${encodeURIComponent(contractId)}`, {}, this._token(token), { user: contractId, }, ); }; approveTask = ( id: string, token: string | undefined, ): Promise> => this.post( `${this._tasks()}/${encodeURIComponent(id)}/approve`, {}, {}, this._token(token), ); denyTask = ( id: string, token: string | undefined, ): Promise> => this.post( `${this._tasks()}/${encodeURIComponent(id)}/deny`, {}, {}, this._token(token), ); // --------------------------------- Data Access Requests -------------------------------------- private _dataAccessRequests = (): string => `${this.baseUrl}/datarequests`; getDataRequestsForDataProduct = ( resourceType: string, resourceId: string, did: number, token: string | undefined, ): Promise => this.get( `${this._dataAccessRequests()}/lite/resourceType/${encodeURIComponent(resourceType)}/resourceId/${encodeURIComponent(resourceId)}/did/${encodeURIComponent(did)}`, {}, this._token(token), ); createDataAccessRequest = ( requestPayload: PlainObject, token: string | undefined, ): Promise => this.post( `${this._dataAccessRequests()}`, requestPayload, undefined, this._token(token), ); createPermitDataRequest = ( payload: PlainObject, token: string | undefined, ): Promise => this.post( `${this._dataAccessRequests()}`, payload, undefined, this._token(token), ); getDataAccessRequestsCreatedBy = ( userId: string, token: string | undefined, ): Promise => this.get( `${this._dataAccessRequests()}/withWorkflow/createdBy/${userId}`, {}, this._token(token), ); getDataAccessRequestWithWorkflow = ( accessRequestId: string, token: string | undefined, ): Promise> => this.get( `${this._dataAccessRequests()}/${encodeURIComponent(accessRequestId)}/withWorkflow`, {}, this._token(token), ); getDataAccessRequestTasks = ( accessRequestId: string, token: string | undefined, ): Promise => this.get( `${this.baseUrl}/datarequests/${encodeURIComponent(accessRequestId)}/tasks`, {}, this._token(token), ); getDataRequestWorkflows = ( accessRequestId: string, token: string | undefined, ): Promise => this.get( `${this._dataAccessRequests()}/${encodeURIComponent(accessRequestId)}/workflows`, {}, this._token(token), ); escalateDataRequest = ( requestId: string, taskId: string, justification: string, token: string | undefined, ): Promise => this.post( `${this._dataAccessRequests()}/${encodeURIComponent(requestId)}/tasks/${encodeURIComponent(taskId)}/escalate?justification=${encodeURIComponent(justification)}`, {}, undefined, this._token(token), ); // TODO: add implementations approveRequest = ( _requestId: string, _taskId: string, _token: string | undefined, ): Promise => Promise.reject(new Error('approveRequest not yet implemented')); // TODO: add implementations denyRequest = ( _requestId: string, _taskId: string, _token: string | undefined, ): Promise => Promise.reject(new Error('denyRequest not yet implemented')); // --------------------------------------- Subscriptions --------------------------------------- private _subscriptions = (): string => `${this.baseUrl}/subscriptions`; getAllSubscriptionsPaginated = ( size: number, lastSubscriptionId: string | undefined, token: string | undefined, ): Promise> => this.get( `${this._subscriptions()}/paginated?size=${size}${lastSubscriptionId ? `&lastSubscriptionId=${encodeURIComponent(lastSubscriptionId)}` : ''}`, {}, this._token(token), ); getSubscriptionsForContract = ( contractId: string, token: string | undefined, ): Promise[]> => this.get( `${this._subscriptions()}/query/contract/${contractId}`, {}, this._token(token), ); createSubscription = ( input: PlainObject, token: string | undefined, ): Promise> => this.post(this._subscriptions(), input, {}, this._token(token)); // --------------------------------------- Data Products --------------------------------------- private _dataProducts = (): string => `${this.baseUrl}/dataproducts`; getDataProductsLitePaginated = ( size: number = 1000, environmentType: V1_EntitlementsLakehouseEnvironmentType, lastDataProductId: string | undefined, lastDataProductDeploymentId: number | undefined, token: string | undefined, ): Promise> => this.get(`${this._dataProducts()}/lite/paginated`, {}, this._token(token), { size, environmentType, lastDataProductId, lastDataProductDeploymentId, }); getAllLiteDataProducts = async ( environmentType: V1_EntitlementsLakehouseEnvironmentType, size: number = 1000, token: string | undefined, ): Promise> => { const allDataProducts: PlainObject[] = []; let lastDataProductId: string | undefined; let lastDataProductDeploymentId: number | undefined; let hasNextPage = true; while (hasNextPage) { const response: PlainObject = await this.getDataProductsLitePaginated( size, environmentType, lastDataProductId, lastDataProductDeploymentId, token, ); const products = ( response.liteDataProductsResponse as | PlainObject | undefined )?.dataProducts ?? []; allDataProducts.push(...(products as PlainObject[])); const metadata = response.paginationMetadataRecord as | PlainObject | undefined; hasNextPage = metadata?.hasNextPage === true; if (hasNextPage && metadata?.lastValuesMap) { const lastValues = metadata.lastValuesMap as PlainObject; lastDataProductId = lastValues.id as string; lastDataProductDeploymentId = lastValues.deployment_id as number; } } return { dataProducts: allDataProducts, } as PlainObject; }; getDataProduct = ( dataProductId: string, token: string | undefined, ): Promise> => this.get( `${this._dataProducts()}/${dataProductId}`, {}, this._token(token), ); getDataProductByIdAndDID = ( dataProductId: string, deploymentId: number, token: string | undefined, ): Promise> => this.get( `${this._dataProducts()}/${dataProductId}/deployments/${deploymentId}`, {}, this._token(token), ); // --------------------------------------- Org Resolver --------------------------------------- private _orgResolver = (): string => `${this.baseUrl}/orgResolver`; getUserEntitlementEnvs = ( userId: string, token: string | undefined, ): Promise => this.get( `${this._orgResolver()}/${userId}/lakehouse/environment`, {}, this._token(token), ); // ------------------------------------- Consumer Entitlements------------------------------------- private _consumerEntitlements = (): string => `${this.baseUrl}/entitle/consumer`; getConsumerGrantsByContractId = ( contractId: string, token: string | undefined, ): Promise> => this.get( `${this._consumerEntitlements()}/sync/audit/grants/contracts/${contractId}`, {}, this._token(token), ); getContractSyncStatus = ( contractId: string, token: string | undefined, ): Promise => this.get( `${this._consumerEntitlements()}/sync/status/contracts/${encodeURIComponent(contractId)}`, {}, this._token(token), ); // ------------------------------------------- Ref Data ------------------------------------------- getOwnersForDid = ( deploymentId: number, token: string | undefined, ): Promise => this.get( `${this.baseUrl}/refData/dataOwner/deploymentId/${deploymentId}`, {}, this._token(token), ); }