Skip to content

Commit

Permalink
feat: support ESR changes for 254 (#6009)
Browse files Browse the repository at this point in the history
* feat: esr modifications for 254

* chore: sort imports

* chore: refine structure

* chore: lint

* chore: catalogedApiVersion null

* chore: missing label

* chore: use max api version from connection
  • Loading branch information
CristiCanizales authored Jan 17, 2025
1 parent 965fa3c commit a41b501
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { notificationService, workspaceUtils } from '@salesforce/salesforcedx-utils-vscode';
import { notificationService, WorkspaceContextUtil, workspaceUtils } from '@salesforce/salesforcedx-utils-vscode';
import { RegistryAccess } from '@salesforce/source-deploy-retrieve-bundle';
import { XMLBuilder, XMLParser } from 'fast-xml-parser';
import * as fs from 'fs';
import * as path from 'path';
import { URL } from 'url';
import * as vscode from 'vscode';
import { parse } from 'yaml';
import { workspaceContext } from '../context';
import { nls } from '../messages';
import { ApexClassOASEligibleResponse, ApexClassOASGatherContextResponse } from '../openApiUtilities/schemas';
import {
ApexClassOASEligibleResponse,
ApexClassOASGatherContextResponse,
ApexOASInfo
} from '../openApiUtilities/schemas';
import { getTelemetryService } from '../telemetry/telemetry';
import { MetadataOrchestrator } from './metadataOrchestrator';

Expand Down Expand Up @@ -129,7 +134,7 @@ export class ApexActionController {
}
const namedCredential = await this.showNamedCredentialsQuickPick();

const updatedContent = this.buildESRXml(existingContent, fullPath, namedCredential, oasSpec);
const updatedContent = await this.buildESRXml(existingContent, fullPath, namedCredential, oasSpec);
try {
// Step 3: Write File
fs.writeFileSync(fullPath, updatedContent);
Expand Down Expand Up @@ -230,14 +235,19 @@ export class ApexActionController {
return finalNamedCredential;
};

private buildESRXml = (
private buildESRXml = async (
existingContent: string | undefined,
fullPath: string,
namedCredential: string | undefined,
oasSpec: string
) => {
const baseName = path.basename(fullPath).split('.')[0];
const safeOasSpec = oasSpec.replaceAll('"', ''').replaceAll('type: Id', 'type: string');
const { description, version } = this.extractInfoProperties(safeOasSpec);
const orgVersion = await (await WorkspaceContextUtil.getInstance().getConnection()).retrieveMaxApiVersion();
if (!orgVersion) {
throw new Error(nls.localize('error_retrieving_org_version'));
}

const parser = new XMLParser({ ignoreAttributes: false });
let jsonObj;
Expand All @@ -256,16 +266,29 @@ export class ApexActionController {
'?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' },
ExternalServiceRegistration: {
'@_xmlns': 'http://soap.sforce.com/2006/04/metadata',
description: `${baseName} External Service`,
description,
label: baseName,
namedCredentialReference: namedCredential ?? 'Type here the Named Credential',
registrationProviderType: 'Custom',
schema: safeOasSpec,
schemaType: 'OpenApi3',
schemaUploadFileExtension: 'yaml',
schemaUploadFileName: `${baseName.toLowerCase()}_openapi`,
status: 'Complete',
systemVersion: '3'
systemVersion: '3',
registrationProvider: baseName,
...(this.isVersionGte(orgVersion, '63.0') // Guarded inclusion for API version 254 and above (instance api version 63.0 and above)
? {
registrationProviderType: 'ApexRest',
namedCredential: null,
namedCredentialReferenceId: null,
catalogedApiVersion: null,
isStartSchemaVersion: true,
isHeadSchemaVersion: true,
schemaArtifactVersion: version
}
: {
registrationProviderType: 'Custom',
namedCredentialReference: namedCredential
})
}
};
}
Expand All @@ -274,4 +297,21 @@ export class ApexActionController {
const builder = new XMLBuilder({ ignoreAttributes: false, format: true });
return builder.build(jsonObj);
};

private isVersionGte = (version: string, targetVersion: string): boolean => {
const major = parseInt(version.split('.')[0], 10);
const targetMajor = parseInt(targetVersion.split('.')[0], 10);
return major >= targetMajor;
};

private extractInfoProperties = (oasSpec: string): ApexOASInfo => {
const parsed = parse(oasSpec);
if (!parsed?.info?.description || !parsed?.info?.version) {
throw new Error(nls.localize('error_parsing_yaml'));
}
return {
description: parsed?.info?.description,
version: parsed?.info?.version
};
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export class MetadataOrchestrator {
Ensure no sensitive details are included. Decline requests unrelated to OpenAPI v3 specs or asking for sensitive information.`;

const userPrompt =
'Generate an OpenAPI v3 specification for the following Apex class. The OpenAPI v3 specification should be a YAML file. The paths should be in the format of /{ClassName}/{MethodName} for all the @AuraEnabled methods specified. For every `type: object`, generate a `#/components/schemas` entry for that object. The method should have a $ref entry pointing to the generated `#/components/schemas` entry. Only include methods that have the @AuraEnabled annotation in the paths of the OpenAPI v3 specification. I do not want AUTHOR_PLACEHOLDER in the result.';
'Generate an OpenAPI v3 specification for the following Apex class. The OpenAPI v3 specification should be a YAML file. The paths should be in the format of /{ClassName}/{MethodName} for all the @AuraEnabled methods specified. For every `type: object`, generate a `#/components/schemas` entry for that object. The method should have a $ref entry pointing to the generated `#/components/schemas` entry. Only include methods that have the @AuraEnabled annotation in the paths of the OpenAPI v3 specification. I do not want AUTHOR_PLACEHOLDER in the result. Do not forget info.description property';

const systemTag = '<|system|>';
const endOfPromptTag = '<|endofprompt|>';
Expand Down
4 changes: 3 additions & 1 deletion packages/salesforcedx-vscode-apex/src/messages/i18n.ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,7 @@ export const messages = {
enter_nc_name: 'Enter the name of the Named Credential',
registry_access_failed: 'Failed to retrieve ESR directory name from the registry.',
full_path_failed: 'Failed to determine the full path for the OpenAPI document.',
schema_element_not_found: 'The <schema> element was not found in the provided XML.'
schema_element_not_found: 'The <schema> element was not found in the provided XML.',
error_retrieving_org_version: 'Failed to retrieve org version',
error_parsing_yaml: 'Error parsing YAML'
};
4 changes: 3 additions & 1 deletion packages/salesforcedx-vscode-apex/src/messages/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,7 @@ export const messages = {
enter_nc_name: 'Enter the name of the Named Credential',
registry_access_failed: 'Failed to retrieve ESR directory name from the registry.',
full_path_failed: 'Failed to determine the full path for the OpenAPI document.',
schema_element_not_found: 'The <schema> element was not found in the provided XML.'
schema_element_not_found: 'The <schema> element was not found in the provided XML.',
error_retrieving_org_version: 'Failed to retrieve org version',
error_parsing_yaml: 'Error parsing YAML'
};
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export enum ApexOASResource {
singleMethodOrProp = 'METHOD or PROPERTY',
folder = 'FOLDER'
}

export type ApexOASInfo = {
description: string;
version: string;
};
// export interface ApexClassOASFilter {
// modifiers: string[] | null;
// }
Expand Down

0 comments on commit a41b501

Please sign in to comment.