UNPKG

dumcp

Version:
297 lines (253 loc) 11.4 kB
import { ConfidentialClientApplication } from '@azure/msal-node'; import axios from 'axios'; // export interface PowerPlatformConfig { // organizationUrl: string; // clientId: string; // clientSecret: string; // tenantId: string; // } // Interface for API responses with value collections export interface ApiCollectionResponse<T> { // value: T[]; // [key: string]: any; // For any additional properties // } export class PowerPlatformService { // private config: PowerPlatformConfig; // private msalClient: ConfidentialClientApplication; // private accessToken: string | null = null; // private tokenExpirationTime: number = 0; // constructor(config: PowerPlatformConfig) { // this.config = config; // // Initialize MSAL client this.msalClient = new ConfidentialClientApplication({ // auth: { // clientId: this.config.clientId, // clientSecret: this.config.clientSecret, // authority: `https://login.microsoftonline.com/${this.config.tenantId}`, // } }); } /** * Get an access token for the PowerPlatform API */ private async getAccessToken(): Promise<string> { // const currentTime = Date.now(); // // If we have a token that isn't expired, return it if (this.accessToken && this.tokenExpirationTime > currentTime) { // return this.accessToken; // } try { // // Get a new token const result = await this.msalClient.acquireTokenByClientCredential({ // scopes: [`${this.config.organizationUrl}/.default`], // }); if (!result || !result.accessToken) { // throw new Error('Failed to acquire access token'); // } this.accessToken = result.accessToken; // // Set expiration time (subtract 5 minutes to refresh early) if (result.expiresOn) { // this.tokenExpirationTime = result.expiresOn.getTime() - (5 * 60 * 1000); // } return this.accessToken; // } catch (error) { // console.error('Error acquiring access token:', error); // throw new Error('Authentication failed'); // } } /** * Make an authenticated request to the PowerPlatform API */ private async makeRequest<T>(endpoint: string, method: 'GET' | 'POST' | 'PATCH' | 'DELETE' = 'GET', data?: any): Promise<T> { try { const token = await this.getAccessToken(); // const requestOptions = { method: method, url: `${this.config.organizationUrl}/${endpoint}`, // headers: { // 'Authorization': `Bearer ${token}`, // 'Accept': 'application/json', // 'OData-MaxVersion': '4.0', // 'OData-Version': '4.0', // 'Content-Type': 'application/json' // Add Content-Type for POST/PATCH }, data: data // Include data for POST/PATCH requests }; const response = await axios(requestOptions); // return response.data as T; // } catch (error: any) { console.error('PowerPlatform API request failed:', error.response?.data || error.message); // More detailed error logging throw new Error(`PowerPlatform API request failed: ${error.response?.data?.error?.message || error.message}`); } } /** * Get metadata about an entity * @param entityName The logical name of the entity */ async getEntityMetadata(entityName: string): Promise<any> { // const response = await this.makeRequest(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')`); // // Remove Privileges property if it exists if (response && typeof response === 'object' && 'Privileges' in response) { // delete response.Privileges; // } return response; // } /** * Get metadata about entity attributes/fields * @param entityName The logical name of the entity */ async getEntityAttributes(entityName: string): Promise<ApiCollectionResponse<any>> { // const selectProperties = [ // 'LogicalName', // ].join(','); // // Make the request to get attributes const response = await this.makeRequest<ApiCollectionResponse<any>>(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/Attributes?$select=${selectProperties}&$filter=AttributeType ne 'Virtual'`); // if (response && response.value) { // // First pass: Filter out attributes that end with 'yominame' response.value = response.value.filter((attribute: any) => { // const logicalName = attribute.LogicalName || ''; // return !logicalName.endsWith('yominame'); // }); // Filter out attributes that end with 'name' if there is another attribute with the same name without the 'name' suffix const baseNames = new Set<string>(); // const namesAttributes = new Map<string, any>(); // for (const attribute of response.value) { // const logicalName = attribute.LogicalName || ''; // if (logicalName.endsWith('name') && logicalName.length > 4) { // const baseName = logicalName.slice(0, -4); // Remove 'name' suffix // namesAttributes.set(baseName, attribute); // } else { // This is a potential base attribute baseNames.add(logicalName); // } } // Find attributes to remove that match the pattern const attributesToRemove = new Set<any>(); // for (const [baseName, nameAttribute] of namesAttributes.entries()) { // if (baseNames.has(baseName)) { // attributesToRemove.add(nameAttribute); // } } response.value = response.value.filter(attribute => !attributesToRemove.has(attribute)); // } return response; // } /** * Get metadata about a specific entity attribute/field * @param entityName The logical name of the entity * @param attributeName The logical name of the attribute */ async getEntityAttribute(entityName: string, attributeName: string): Promise<any> { // return this.makeRequest(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/Attributes(LogicalName='${attributeName}')`); // } /** * Get one-to-many relationships for an entity * @param entityName The logical name of the entity */ async getEntityOneToManyRelationships(entityName: string): Promise<ApiCollectionResponse<any>> { // const selectProperties = [ // 'SchemaName', // 'RelationshipType', // 'ReferencedAttribute', // 'ReferencedEntity', // 'ReferencingAttribute', // 'ReferencingEntity', // 'ReferencedEntityNavigationPropertyName', // 'ReferencingEntityNavigationPropertyName' // ].join(','); // // Only filter by ReferencingAttribute in the OData query since startswith isn't supported const response = await this.makeRequest<ApiCollectionResponse<any>>(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/OneToManyRelationships?$select=${selectProperties}&$filter=ReferencingAttribute ne 'regardingobjectid'`); // // Filter the response to exclude relationships with ReferencingEntity starting with 'msdyn_' or 'adx_' if (response && response.value) { // response.value = response.value.filter((relationship: any) => { // const referencingEntity = relationship.ReferencingEntity || ''; // return !(referencingEntity.startsWith('msdyn_') || referencingEntity.startsWith('adx_')); // }); } return response; // } /** * Get many-to-many relationships for an entity * @param entityName The logical name of the entity */ async getEntityManyToManyRelationships(entityName: string): Promise<ApiCollectionResponse<any>> { // const selectProperties = [ // 'SchemaName', // 'RelationshipType', // 'Entity1LogicalName', // 'Entity2LogicalName', // 'Entity1IntersectAttribute', // 'Entity2IntersectAttribute', // 'Entity1NavigationPropertyName', // 'Entity2NavigationPropertyName' // ].join(','); // return this.makeRequest<ApiCollectionResponse<any>>(`api/data/v9.2/EntityDefinitions(LogicalName='${entityName}')/ManyToManyRelationships?$select=${selectProperties}`); // } /** * Get all relationships (one-to-many and many-to-many) for an entity * @param entityName The logical name of the entity */ async getEntityRelationships(entityName: string): Promise<{oneToMany: ApiCollectionResponse<any>, manyToMany: ApiCollectionResponse<any>}> { // const [oneToMany, manyToMany] = await Promise.all([ // this.getEntityOneToManyRelationships(entityName), // this.getEntityManyToManyRelationships(entityName) // ]); return { // oneToMany, // manyToMany // }; } /** * Get a global option set definition by name * @param optionSetName The name of the global option set * @returns The global option set definition */ async getGlobalOptionSet(optionSetName: string): Promise<any> { // return this.makeRequest(`api/data/v9.2/GlobalOptionSetDefinitions(Name='${optionSetName}')`); // } /** * Get a specific record by entity name (plural) and ID * @param entityNamePlural The plural name of the entity (e.g., 'accounts', 'contacts') * @param recordId The GUID of the record * @returns The record data */ async getRecord(entityNamePlural: string, recordId: string): Promise<any> { // return this.makeRequest(`api/data/v9.2/${entityNamePlural}(${recordId})`); // } /** * Query records using entity name (plural) and a filter expression * @param entityNamePlural The plural name of the entity (e.g., 'accounts', 'contacts') * @param filter OData filter expression (e.g., "name eq 'test'") * @param maxRecords Maximum number of records to retrieve (default: 50) * @returns Filtered list of records */ async queryRecords(entityNamePlural: string, filter: string, maxRecords: number = 50): Promise<ApiCollectionResponse<any>> { // return this.makeRequest<ApiCollectionResponse<any>>(`api/data/v9.2/${entityNamePlural}?$filter=${encodeURIComponent(filter)}&$top=${maxRecords}`); // } /** * Create a new record in a Power Platform entity * @param entityNamePlural The plural name of the entity (e.g., 'accounts', 'contacts') * @param recordData The data for the new record as a JSON object * @returns The created record's data (often includes the new ID) */ async createRecord(entityNamePlural: string, recordData: any): Promise<any> { return this.makeRequest(`api/data/v9.2/${entityNamePlural}`, 'POST', recordData); } /** * Update an existing record in a Power Platform entity * @param entityNamePlural The plural name of the entity * @param recordId The GUID of the record to update * @param recordData The data to update in the record as a JSON object */ async updateRecord(entityNamePlural: string, recordId: string, recordData: any): Promise<void> { await this.makeRequest(`api/data/v9.2/${entityNamePlural}(${recordId})`, 'PATCH', recordData); } /** * Delete a record from a Power Platform entity by ID * @param entityNamePlural The plural name of the entity * @param recordId The GUID of the record to delete */ async deleteRecord(entityNamePlural: string, recordId: string): Promise<void> { await this.makeRequest(`api/data/v9.2/${entityNamePlural}(${recordId})`, 'DELETE'); } }