push neon reactor
This commit is contained in:
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
export interface ApiResponseInfo {
|
||||
type?: string;
|
||||
format?: string;
|
||||
}
|
||||
export interface ApiParameter {
|
||||
name: string;
|
||||
in: string;
|
||||
required: boolean;
|
||||
type: string;
|
||||
format?: string;
|
||||
}
|
||||
export interface ApiDefinition {
|
||||
path: string;
|
||||
method: string;
|
||||
parameters?: ApiParameter[];
|
||||
responseInfo?: Record<string, ApiResponseInfo>;
|
||||
}
|
||||
export interface DataSourceInfo {
|
||||
tableId: string;
|
||||
version?: string;
|
||||
primaryKey?: string;
|
||||
dataSourceType?: string;
|
||||
apis: Record<string, ApiDefinition>;
|
||||
}
|
||||
export type DataSourcesInfo = Record<string, DataSourceInfo>;
|
||||
//# sourceMappingURL=dataSourceInfo.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dataSourceInfo.d.ts","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAChD;AAMD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACrC;AAKD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC"}
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
export {};
|
||||
//# sourceMappingURL=dataSourceInfo.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dataSourceInfo.js","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ILogger } from '../services/index.js';
|
||||
/**
|
||||
* Processes all JSON files in the specified folder and generates a TypeScript file with data source information.
|
||||
* @param schemaFolderPath The path to the folder containing JSON files.
|
||||
*/
|
||||
export declare function processDataSourceInfo(schemaFolderPath: string, logger: ILogger): Promise<void>;
|
||||
//# sourceMappingURL=dataSourceInfoProcessor.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dataSourceInfoProcessor.d.ts","sourceRoot":"","sources":["../../src/CodeGen/dataSourceInfoProcessor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAiB3C;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC,CAmDf"}
|
||||
Generated
Vendored
+555
@@ -0,0 +1,555 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { getVfs } from '../VfsManager.js';
|
||||
import { isDataverseEntity } from './shared/dataverseUtils.js';
|
||||
import { extractModelName } from './shared/modelUtils.js';
|
||||
import { getAllJsonFiles, isJsonObject } from './shared/schemaUtils.js';
|
||||
import { getSqlStoredProcApiKey } from './shared/sqlUtils.js';
|
||||
/** Regex to match file extension (e.g., ".json", ".ts") */
|
||||
const FILE_EXTENSION_PATTERN = /\.[^/.]+$/;
|
||||
/**
|
||||
* Processes all JSON files in the specified folder and generates a TypeScript file with data source information.
|
||||
* @param schemaFolderPath The path to the folder containing JSON files.
|
||||
*/
|
||||
export async function processDataSourceInfo(schemaFolderPath, logger) {
|
||||
const scenario = logger.trackScenario('ProcessDataSourceInfo', { schemaFolderPath });
|
||||
const vfs = getVfs();
|
||||
try {
|
||||
const jsonFiles = await getAllJsonFiles(schemaFolderPath);
|
||||
const resultJson = {};
|
||||
for (const filePath of jsonFiles) {
|
||||
try {
|
||||
const jsonContent = await vfs.readFile(filePath, 'utf-8');
|
||||
const jsonDocument = JSON.parse(jsonContent);
|
||||
const dataSourceInfo = processJson(jsonDocument);
|
||||
// note: this should use the same function as used in modelServiceGenerator to avoid mismatches
|
||||
const propertyKey = determinePropertyKey(jsonDocument, filePath, vfs, logger);
|
||||
mergeDataSourceInfo(resultJson, propertyKey, dataSourceInfo);
|
||||
}
|
||||
catch (err) {
|
||||
const error = err;
|
||||
throw new Error(`Error processing file '${filePath}': ${error.message}`);
|
||||
}
|
||||
}
|
||||
const outputJson = JSON.stringify(resultJson, null, 2); // pretty print
|
||||
const finalOutput = `/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
* This file is auto-generated. Do not modify it manually.
|
||||
* Changes to this file may be overwritten.
|
||||
*/
|
||||
|
||||
export const dataSourcesInfo = ${outputJson};
|
||||
`;
|
||||
const outputDir = vfs.join(schemaFolderPath, 'appschemas');
|
||||
const outputFilePath = vfs.join(outputDir, 'dataSourcesInfo.ts');
|
||||
// Create the appschemas directory if it doesn't exist
|
||||
if (!(await vfs.exists(outputDir))) {
|
||||
await vfs.mkdir(outputDir, { recursive: true });
|
||||
}
|
||||
await vfs.writeFile(outputFilePath, finalOutput);
|
||||
scenario.complete();
|
||||
}
|
||||
catch (error) {
|
||||
scenario.failure({ error });
|
||||
throw new Error(`An unexpected error occurred: ${error.message}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Merges new data source information into the result JSON object.
|
||||
* If the property key already exists, it merges the APIs, avoiding duplicates.
|
||||
* @param resultJson The result JSON object to merge into.
|
||||
* @param propertyKey The key under which to store the data source information.
|
||||
* @param newDataSourceInfo The new data source information to merge.
|
||||
*/
|
||||
function mergeDataSourceInfo(resultJson, propertyKey, newDataSourceInfo) {
|
||||
if (Object.prototype.hasOwnProperty.call(resultJson, propertyKey)) {
|
||||
const existingDataSource = resultJson[propertyKey];
|
||||
if (existingDataSource && existingDataSource.apis) {
|
||||
for (const apiKey in newDataSourceInfo.apis) {
|
||||
if (!Object.prototype.hasOwnProperty.call(existingDataSource.apis, apiKey)) {
|
||||
existingDataSource.apis[apiKey] = newDataSourceInfo.apis[apiKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(`The existing data source for key '${propertyKey}' is not of type DataSourceInfo.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultJson[propertyKey] = newDataSourceInfo;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines the property key for a data source, falling back to filename if extraction fails.
|
||||
* @param jsonDocument The parsed JSON document.
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param vfs The virtual file system instance.
|
||||
* @param logger The logger instance for telemetry.
|
||||
* @returns The property key to use for the data source.
|
||||
*/
|
||||
function determinePropertyKey(jsonDocument, filePath, vfs, logger) {
|
||||
try {
|
||||
return extractModelName(jsonDocument, filePath, vfs).dataSourceName.toLowerCase();
|
||||
}
|
||||
catch (error) {
|
||||
logger.trackActivityEvent('DataSourceInfoProcessor_FallbackName', {
|
||||
filePath,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
// fall back to using the file name without extension
|
||||
const fileName = vfs.basename(filePath);
|
||||
return fileName.replace(FILE_EXTENSION_PATTERN, '').toLowerCase();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Processes the JSON document and extracts relevant data source information.
|
||||
* @param json The JSON document to process.
|
||||
* @param existingDataSourceInfo Optional existing data source information to merge with.
|
||||
* @returns The processed data source information.
|
||||
*/
|
||||
function processJson(json, existingDataSourceInfo) {
|
||||
const root = json;
|
||||
if (typeof root.type === 'string' && root.type === 'Microsoft.PowerApps/apis') {
|
||||
const dataSourceInfo = {
|
||||
tableId: '',
|
||||
version: '',
|
||||
primaryKey: '',
|
||||
dataSourceType: 'Connector',
|
||||
apis: {},
|
||||
};
|
||||
const properties = isJsonObject(root.properties) ? root.properties : null;
|
||||
const swagger = properties && isJsonObject(properties.swagger) ? properties.swagger : null;
|
||||
const paths = swagger && isJsonObject(swagger.paths) ? swagger.paths : null;
|
||||
if (paths) {
|
||||
for (const [pathKey, pathValue] of Object.entries(paths)) {
|
||||
if (isJsonObject(pathValue)) {
|
||||
for (const [methodKey, methodValue] of Object.entries(pathValue)) {
|
||||
if (isJsonObject(methodValue)) {
|
||||
const operation = methodValue;
|
||||
const operationId = operation.operationId;
|
||||
if (typeof operationId === 'string') {
|
||||
const apiDefinition = {
|
||||
path: pathKey,
|
||||
method: methodKey.toUpperCase(),
|
||||
};
|
||||
apiDefinition.parameters =
|
||||
operation.parameters && Array.isArray(operation.parameters)
|
||||
? processParameters(operation.parameters, swagger)
|
||||
: [];
|
||||
if (operation.responses) {
|
||||
apiDefinition.responseInfo = processResponse(operation.responses, swagger);
|
||||
}
|
||||
else {
|
||||
apiDefinition.responseInfo = {
|
||||
default: { type: 'void', format: undefined },
|
||||
};
|
||||
}
|
||||
dataSourceInfo.apis[operationId] = apiDefinition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataSourceInfo;
|
||||
}
|
||||
else if (isJsonObject(root.schema)) {
|
||||
const schema = root.schema;
|
||||
if (isDataverseEntity(root)) {
|
||||
const items = schema.items;
|
||||
// This is a Dataverse entity schema
|
||||
const primaryKey = typeof items['x-ms-dataverse-primary-id'] === 'string'
|
||||
? items['x-ms-dataverse-primary-id']
|
||||
: '';
|
||||
return {
|
||||
tableId: '',
|
||||
version: '',
|
||||
primaryKey,
|
||||
dataSourceType: 'Dataverse',
|
||||
apis: {},
|
||||
};
|
||||
}
|
||||
if (typeof schema.procedureresultschema !== 'undefined') {
|
||||
const dataSourceInfo = existingDataSourceInfo ?? {
|
||||
tableId: '',
|
||||
version: 'v2',
|
||||
primaryKey: '',
|
||||
dataSourceType: 'Connector',
|
||||
apis: {},
|
||||
};
|
||||
const procedureName = typeof root.name === 'string' ? root.name : '';
|
||||
const cleanedProcedureName = '/' + procedureName.replace(/\[|\]/g, '');
|
||||
const cleanedProcedureNameWithoutSchema = getSqlStoredProcApiKey(procedureName);
|
||||
let parameters = [];
|
||||
if (isJsonObject(schema.inputparameters) && isJsonObject(schema.inputparameters.properties)) {
|
||||
// Stored procedure parameters are wrapped under 'inputParameters' by the codegen.
|
||||
// Use a single body param with that name so _buildOperationBodyParam can find the value.
|
||||
parameters = [{ name: 'inputParameters', in: 'body', required: false, type: 'object' }];
|
||||
}
|
||||
const apiDefinition = {
|
||||
path: cleanedProcedureName,
|
||||
method: 'POST',
|
||||
parameters,
|
||||
};
|
||||
if (!dataSourceInfo.apis[cleanedProcedureNameWithoutSchema]) {
|
||||
dataSourceInfo.apis[cleanedProcedureNameWithoutSchema] = apiDefinition;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Duplicate stored procedure detected: ${cleanedProcedureName}`);
|
||||
}
|
||||
return dataSourceInfo;
|
||||
}
|
||||
else if (typeof root.name === 'string' && root.name.match(/^\[[^\]]+\]\./)) {
|
||||
// Use the explicit version annotation injected at add-time when available.
|
||||
// Fall back to 'v2' for backward compatibility with schema files created before this annotation was added.
|
||||
const version = typeof root['x-ms-connection-version'] === 'string'
|
||||
? root['x-ms-connection-version']
|
||||
: 'v2';
|
||||
return {
|
||||
tableId: root.name,
|
||||
version,
|
||||
primaryKey: getPrimaryKeyFromSchema(schema),
|
||||
dataSourceType: 'Connector',
|
||||
apis: {},
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Sharepoint List
|
||||
const tableId = typeof root.name === 'string' && root.name !== null ? root.name : '';
|
||||
const dataSourceInfo = {
|
||||
tableId,
|
||||
version: '',
|
||||
primaryKey: getPrimaryKeyFromSchema(schema),
|
||||
dataSourceType: 'Connector',
|
||||
apis: {},
|
||||
};
|
||||
const referencedEntities = isJsonObject(root.referencedEntities)
|
||||
? root.referencedEntities
|
||||
: null;
|
||||
if (referencedEntities) {
|
||||
for (const referencedEntityKey in referencedEntities) {
|
||||
if (!Object.prototype.hasOwnProperty.call(referencedEntities, referencedEntityKey)) {
|
||||
continue;
|
||||
}
|
||||
const referencedEntityValue = referencedEntities[referencedEntityKey];
|
||||
if (isJsonObject(referencedEntityValue) &&
|
||||
referencedEntityValue.lookupEndpoint !== null) {
|
||||
const lookupEndpointProps = referencedEntityValue.lookupEndpoint;
|
||||
if (isJsonObject(lookupEndpointProps) && lookupEndpointProps.path !== null) {
|
||||
const lookupEndpointPath = lookupEndpointProps.path;
|
||||
if (typeof lookupEndpointPath === 'string') {
|
||||
const segments = lookupEndpointPath.split('/');
|
||||
// Process the segments as needed
|
||||
segments[segments.length - 1] = referencedEntityKey; // Replace last segment
|
||||
// in the updated segment remove hyphens in the GUIDs
|
||||
// This might be an SPO connector issue, but we need to work around it here.
|
||||
// ex: "/tables/05bfc50c-b51b-4c17-aac4-45bd5dbb9532/entities/Author",
|
||||
// becomes "/tables/05bfc50cb51b4c17aac445bd5dbb9532/entities/Author"
|
||||
const guidRegex = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g;
|
||||
const updatedSegment = segments
|
||||
.join('/')
|
||||
.replace(guidRegex, (match) => match.replace(/-/g, ''));
|
||||
const finalPath = `/{connectionId}/datasets/{dataset}${updatedSegment}`;
|
||||
const apiParameterList = new Array();
|
||||
const defaultApiParameters = ['connectionId', 'dataset', 'table'];
|
||||
const defaultQueryParameters = ['search'];
|
||||
for (const param of defaultApiParameters) {
|
||||
const apiParameter = {
|
||||
name: param,
|
||||
in: 'path',
|
||||
required: true,
|
||||
type: 'string',
|
||||
};
|
||||
apiParameterList.push(apiParameter);
|
||||
}
|
||||
for (const param of defaultQueryParameters) {
|
||||
const apiParameter = {
|
||||
name: param,
|
||||
in: 'query',
|
||||
required: false,
|
||||
type: 'string',
|
||||
};
|
||||
apiParameterList.push(apiParameter);
|
||||
}
|
||||
const apiResponseInfo = {
|
||||
'200': { type: 'array' },
|
||||
};
|
||||
const apiDefinition = {
|
||||
path: finalPath,
|
||||
method: 'GET',
|
||||
parameters: apiParameterList,
|
||||
responseInfo: apiResponseInfo,
|
||||
};
|
||||
const referencedEntityKeyNormalized = normalizeHyphenatedName(referencedEntityKey);
|
||||
const operationId = `Get${referencedEntityKeyNormalized}`;
|
||||
if (dataSourceInfo.apis[operationId]) {
|
||||
throw new Error(`Duplicate operationId detected: ${operationId}`);
|
||||
}
|
||||
dataSourceInfo.apis[operationId] = apiDefinition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataSourceInfo;
|
||||
}
|
||||
}
|
||||
if (typeof root.type === 'string' && root.type === 'Microsoft.PowerApps/dataverseOperation') {
|
||||
return processDataverseOperation(root);
|
||||
}
|
||||
throw new Error('The JSON does not represent a valid data source.');
|
||||
}
|
||||
/**
|
||||
* Processes the responses node and extracts response information.
|
||||
* @param responsesNode The responses node from the Swagger document.
|
||||
* @param root The root of the Swagger document for reference.
|
||||
* @returns A record of response information keyed by status code.
|
||||
*/
|
||||
function processResponse(responses, root) {
|
||||
const result = {};
|
||||
// Type guard: ensure responsesNode is an object
|
||||
if (!isJsonObject(responses)) {
|
||||
return result;
|
||||
}
|
||||
for (const statusCode in responses) {
|
||||
if (!Object.prototype.hasOwnProperty.call(responses, statusCode)) {
|
||||
continue;
|
||||
}
|
||||
const respObj = responses[statusCode];
|
||||
// Type guard: ensure resp is an object
|
||||
if (isJsonObject(respObj)) {
|
||||
if (isJsonObject(respObj.schema)) {
|
||||
const schemaNode = respObj.schema;
|
||||
const type = typeof schemaNode.type === 'string' ? schemaNode.type : null;
|
||||
const format = typeof schemaNode.format === 'string' ? schemaNode.format : null;
|
||||
// If type is array, always return "array" as type
|
||||
if (type === 'array') {
|
||||
result[statusCode] = { type: 'array', format: format || undefined };
|
||||
continue;
|
||||
}
|
||||
// $ref (single object)
|
||||
// eslint-disable-next-line dot-notation
|
||||
if (schemaNode['$ref']) {
|
||||
// eslint-disable-next-line dot-notation
|
||||
const refValue = schemaNode['$ref'];
|
||||
if (typeof refValue === 'string' && refValue.startsWith('#/definitions/')) {
|
||||
const defName = refValue.substring('#/definitions/'.length);
|
||||
let refType = 'object';
|
||||
// Type guard for root to access definitions
|
||||
if (isJsonObject(root)) {
|
||||
if (isJsonObject(root.definitions)) {
|
||||
const definitions = root.definitions;
|
||||
const defNode = definitions[defName];
|
||||
if (isJsonObject(defNode)) {
|
||||
refType = typeof defNode.type === 'string' ? defNode.type : 'object';
|
||||
}
|
||||
}
|
||||
}
|
||||
result[statusCode] = { type: refType, format: format || undefined };
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Primitive or object
|
||||
result[statusCode] = { type: type || 'object', format: format || undefined };
|
||||
}
|
||||
else {
|
||||
// No schema, but description exists
|
||||
result[statusCode] = { type: 'void', format: undefined };
|
||||
}
|
||||
}
|
||||
else {
|
||||
// resp is not an object, treat as void
|
||||
result[statusCode] = { type: 'void', format: undefined };
|
||||
}
|
||||
}
|
||||
// If no responses at all, return a default void
|
||||
if (Object.keys(result).length === 0) {
|
||||
result.default = { type: 'void', format: undefined };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Retrieves the primary key from the schema node.
|
||||
* @param schemaNode The schema node to extract the primary key from.
|
||||
* @returns The primary key as a string, or an empty string if not found.
|
||||
*/
|
||||
function getPrimaryKeyFromSchema(schema) {
|
||||
// Type guard: ensure schemaNode. items exists and is an object. properties exists and is an object
|
||||
if (!isJsonObject(schema) ||
|
||||
!isJsonObject(schema.items) ||
|
||||
!isJsonObject(schema.items.properties)) {
|
||||
return '';
|
||||
}
|
||||
const propertiesNode = schema.items.properties;
|
||||
for (const propertyName in propertiesNode) {
|
||||
if (!Object.prototype.hasOwnProperty.call(propertiesNode, propertyName)) {
|
||||
continue;
|
||||
}
|
||||
const propertyValue = propertiesNode[propertyName];
|
||||
// Type guard: ensure propertyValue is an object
|
||||
if (isJsonObject(propertyValue)) {
|
||||
if (propertyValue['x-ms-keyType'] === 'primary') {
|
||||
return propertyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Processes the parameters node and extracts parameter information.
|
||||
* @param parametersNode The parameters node from the Swagger document.
|
||||
* @param root The root of the Swagger document for reference.
|
||||
* @returns An array of ApiParameter objects representing the parameters.
|
||||
*/
|
||||
function processParameters(parametersNode, root) {
|
||||
const parameters = [];
|
||||
// Type guard: ensure parametersNode is an array
|
||||
if (!Array.isArray(parametersNode)) {
|
||||
return parameters;
|
||||
}
|
||||
for (const parameter of parametersNode) {
|
||||
// Type guard: ensure parameter is an object
|
||||
if (isJsonObject(parameter)) {
|
||||
if (parameter.$ref) {
|
||||
const refPath = parameter.$ref;
|
||||
if (typeof refPath === 'string') {
|
||||
const resolvedParameter = resolveRef(refPath, root);
|
||||
if (resolvedParameter) {
|
||||
parameters.push(parseParameter(resolvedParameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
parameters.push(parseParameter(parameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stable sort: required=true first, then required=false
|
||||
return parameters.sort((a, b) => Number(b.required) - Number(a.required));
|
||||
}
|
||||
/**
|
||||
* Resolves a $ref reference in the Swagger document.
|
||||
* @param refPath The $ref path to resolve.
|
||||
* @param root The root of the Swagger document for reference.
|
||||
* @returns The resolved object or null if not found.
|
||||
*/
|
||||
function resolveRef(refPath, root) {
|
||||
// Type guard: ensure root is an object
|
||||
if (!isJsonObject(root)) {
|
||||
return null;
|
||||
}
|
||||
// Resolve $ref (e.g., "#/swagger/parameters/ParameterName")
|
||||
if (refPath.startsWith('#/')) {
|
||||
const parts = refPath.substring(2).split('/');
|
||||
let currentElement = root;
|
||||
for (const part of parts) {
|
||||
// Type guard: ensure currentElement is an object
|
||||
if (isJsonObject(currentElement)) {
|
||||
if (part in currentElement) {
|
||||
currentElement = currentElement[part];
|
||||
}
|
||||
else {
|
||||
return null; // Reference not found
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null; // Current element is not an object
|
||||
}
|
||||
}
|
||||
// Type guard: ensure final result is an object
|
||||
if (isJsonObject(currentElement)) {
|
||||
return currentElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Parses a parameter object and extracts relevant information.
|
||||
* @param parameter The parameter object to parse.
|
||||
* @returns An ApiParameter object representing the parsed parameter.
|
||||
*/
|
||||
function parseParameter(parameter) {
|
||||
// In OpenAPI 2.0, body parameters encode type/format inside a "schema" sub-object.
|
||||
// Path/query parameters use top-level "type"/"format" fields. Check both locations.
|
||||
const schema = isJsonObject(parameter.schema) ? parameter.schema : null;
|
||||
const type = typeof parameter.type === 'string'
|
||||
? parameter.type
|
||||
: typeof schema?.type === 'string'
|
||||
? schema.type
|
||||
: 'object';
|
||||
const format = typeof parameter.format === 'string'
|
||||
? parameter.format
|
||||
: typeof schema?.format === 'string'
|
||||
? schema.format
|
||||
: undefined;
|
||||
return {
|
||||
name: typeof parameter.name === 'string' ? parameter.name : '',
|
||||
in: typeof parameter.in === 'string' ? parameter.in : '',
|
||||
required: parameter.required === true,
|
||||
type,
|
||||
format,
|
||||
};
|
||||
}
|
||||
/*
|
||||
* The normalizeHyphenatedName method normalizes hyphenated names.
|
||||
* It removes all hyphens from the name and returns the normalized name.
|
||||
*/
|
||||
function normalizeHyphenatedName(name) {
|
||||
return name.replace(/-/g, '');
|
||||
}
|
||||
/**
|
||||
* Processes a Microsoft.PowerApps/dataverseOperation schema into a DataSourceInfo entry.
|
||||
* Each operation is keyed by its lowercased operation name (e.g. `winopportunity`).
|
||||
*
|
||||
* Reads from the embedded Swagger 2.0 spec produced by `buildDataverseOperationSchema`.
|
||||
* The swagger is nested under `properties.swagger` (connector schema format) so that the
|
||||
* existing `handleSwaggerSchema` codegen path processes it without a separate handler.
|
||||
* All type mappings are already resolved in the swagger at write-time.
|
||||
*/
|
||||
function processDataverseOperation(root) {
|
||||
const props = isJsonObject(root.properties) ? root.properties : null;
|
||||
const swagger = props && isJsonObject(props.swagger) ? props.swagger : null;
|
||||
if (!swagger) {
|
||||
throw new Error('Dataverse operation schema is missing the required "properties.swagger" field.');
|
||||
}
|
||||
const basePath = typeof swagger.basePath === 'string' ? swagger.basePath : '';
|
||||
const paths = isJsonObject(swagger.paths) ? swagger.paths : null;
|
||||
if (!paths) {
|
||||
throw new Error('Dataverse operation schema swagger is missing "paths".');
|
||||
}
|
||||
const apis = {};
|
||||
for (const [pathKey, pathValue] of Object.entries(paths)) {
|
||||
if (!isJsonObject(pathValue))
|
||||
continue;
|
||||
for (const [methodKey, methodValue] of Object.entries(pathValue)) {
|
||||
if (!isJsonObject(methodValue))
|
||||
continue;
|
||||
const operationId = typeof methodValue.operationId === 'string' ? methodValue.operationId : '';
|
||||
if (!operationId)
|
||||
continue;
|
||||
const parameters = Array.isArray(methodValue.parameters)
|
||||
? methodValue.parameters.filter(isJsonObject).map((param) => ({
|
||||
name: typeof param.name === 'string' ? param.name : '',
|
||||
in: typeof param.in === 'string' ? param.in : 'body',
|
||||
required: param.required === true,
|
||||
type: typeof param.type === 'string' ? param.type : 'object',
|
||||
}))
|
||||
: [];
|
||||
apis[operationId] = {
|
||||
path: basePath + pathKey,
|
||||
method: methodKey.toUpperCase(),
|
||||
parameters,
|
||||
responseInfo: processResponse(methodValue.responses, swagger),
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
tableId: '',
|
||||
version: '',
|
||||
primaryKey: '',
|
||||
dataSourceType: 'Dataverse',
|
||||
apis,
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=dataSourceInfoProcessor.js.map
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ILogger } from '../services/index.js';
|
||||
export interface IGenerateModelServiceOptions {
|
||||
schemaFolderPath: string;
|
||||
schemaFilePath?: string;
|
||||
codeGenPath: string;
|
||||
logger: ILogger;
|
||||
useV2Codegen?: boolean;
|
||||
}
|
||||
export declare function generateModelService(options: IGenerateModelServiceOptions): Promise<void>;
|
||||
/**
|
||||
* Clears and regenerates the model and service files.
|
||||
*/
|
||||
export declare function clearAndRegenerateModelService(options: IGenerateModelServiceOptions): Promise<void>;
|
||||
//# sourceMappingURL=modelServiceGenerator.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"modelServiceGenerator.d.ts","sourceRoot":"","sources":["../../src/CodeGen/modelServiceGenerator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAuD3C,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAYD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD/F;AAED;;GAEG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAiBf"}
|
||||
CodeApp_NeonReactor/node_modules/@microsoft/power-apps-actions/dist/CodeGen/modelServiceGenerator.js
Generated
Vendored
+2830
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
/** Extracts a human-readable display name from CDP connector metadata, for use as the data source label. */
|
||||
export declare function tryExtractDisplayNameFromCdpMetadata(metadata: unknown): string | undefined;
|
||||
//# sourceMappingURL=cdpUtils.d.ts.map
|
||||
CodeApp_NeonReactor/node_modules/@microsoft/power-apps-actions/dist/CodeGen/shared/cdpUtils.d.ts.map
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cdpUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/cdpUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,4GAA4G;AAC5G,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CA2B1F"}
|
||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { isJsonObject } from './schemaUtils.js';
|
||||
/** Extracts a human-readable display name from CDP connector metadata, for use as the data source label. */
|
||||
export function tryExtractDisplayNameFromCdpMetadata(metadata) {
|
||||
// Assuming metadata contains a 'name' or similar field for the stored procedure
|
||||
try {
|
||||
// If metadata is a string, try to parse it as JSON
|
||||
let json = metadata;
|
||||
if (typeof metadata === 'string') {
|
||||
json = JSON.parse(metadata);
|
||||
}
|
||||
// Use type guards to safely access properties
|
||||
if (isJsonObject(json)) {
|
||||
// Try to get the title first, as it's often more user-friendly
|
||||
if ('title' in json && typeof json.title === 'string') {
|
||||
return json.title;
|
||||
}
|
||||
// Fall back to name if title isn't available
|
||||
if ('name' in json && typeof json.name === 'string') {
|
||||
return json.name;
|
||||
}
|
||||
}
|
||||
// If we can't find a suitable property, use the default name
|
||||
return undefined;
|
||||
}
|
||||
catch (ex) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=cdpUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cdpUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/cdpUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,4GAA4G;AAC5G,MAAM,UAAU,oCAAoC,CAAC,QAAiB;IACpE,gFAAgF;IAChF,IAAI,CAAC;QACH,mDAAmD;QACnD,IAAI,IAAI,GAAY,QAAQ,CAAC;QAC7B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAED,8CAA8C;QAC9C,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,+DAA+D;YAC/D,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACtD,OAAO,IAAI,CAAC,KAAK,CAAC;YACpB,CAAC;YAED,6CAA6C;YAC7C,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC,IAAI,CAAC;YACnB,CAAC;QACH,CAAC;QAED,6DAA6D;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,EAAE,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { JsonObject } from '../../Common/jsonTypes.js';
|
||||
/** Returns true if the schema root represents a Dataverse entity table. */
|
||||
export declare function isDataverseEntity(root: unknown): boolean;
|
||||
/** Returns the Dataverse entity set name for a schema (used as the OData API path), or null if not defined. */
|
||||
export declare function getDataverseEntitySetName(root: unknown): string | null;
|
||||
/** Returns true if the property is a Dataverse lookup relationship to another table. */
|
||||
export declare function isDataverseLookupProperty(propValue: JsonObject): boolean;
|
||||
/** Collects all lookup relationship columns from a properties node, keyed by property name. */
|
||||
export declare function getDataverseLookupColumns(propertiesNode: JsonObject): Map<string, JsonObject>;
|
||||
/** Returns true if the property is an auto-generated name field for a lookup column (e.g. `owneridname` for `ownerid`). */
|
||||
export declare function isLookupRelatedProperty(propName: string, lookupColumns: Map<string, unknown>): boolean;
|
||||
/** Returns the write-form property name for a lookup column, used when creating or updating Dataverse records. */
|
||||
export declare function generateODataBindPropertyName(schemaName: string): string;
|
||||
/** Returns the read-form property name for a lookup's resolved GUID value. */
|
||||
export declare function generateLookupValuePropertyName(lookupPropertyName: string): string;
|
||||
/**
|
||||
* Returns true if the property is a Dataverse boolean (yes/no) field.
|
||||
* These fields have enum/x-ms-enum-values in the schema but must be typed as `boolean`,
|
||||
* not as a numeric optionset, because the Dataverse Web API expects true/false.
|
||||
*/
|
||||
export declare function isDataverseBooleanProperty(propValue: JsonObject): boolean;
|
||||
/**
|
||||
* Returns true if the property is a Dataverse numeric field (integer, decimal, double, money, bigint).
|
||||
* These fields have `"type": "string"` in the schema but must be typed as `number` because
|
||||
* the Dataverse Web API sends and receives them as JSON numbers.
|
||||
*/
|
||||
export declare function isDataverseNumericProperty(propValue: JsonObject): boolean;
|
||||
/**
|
||||
* Returns true if the property is a Dataverse multi-select picklist (multi-choice) field.
|
||||
* These fields allow multiple selections and should be typed as an array of the enum type.
|
||||
*/
|
||||
export declare function isDataverseMultiSelectPicklistProperty(propValue: JsonObject): boolean;
|
||||
/**
|
||||
* Returns the names of all MultiSelectPicklist (multi-choice) properties in a Dataverse entity
|
||||
* properties node — i.e., properties that have optionset data AND are typed as MultiSelectPicklistType.
|
||||
* These fields require serialization (array → comma-separated string) when writing to the Dataverse API
|
||||
* and deserialization (comma-separated string → array) when reading back from it.
|
||||
*/
|
||||
export declare function getDataverseMultiSelectPicklistFields(propertiesNode: JsonObject): string[];
|
||||
/** Returns true if the property represents an optionset (choice) column with enumerable values. */
|
||||
export declare function hasOptionsetData(propValue: JsonObject): boolean;
|
||||
/** Produces the TypeScript identifier used for the enum or const generated from an optionset property. */
|
||||
export declare function generateOptionsetEnumName(modelName: string, propName: string): string;
|
||||
/** Formats an optionset display label as a TypeScript string literal for use in union types and const declarations. */
|
||||
export declare function formatEnumLabel(label: unknown): string;
|
||||
type GenericColumn = {
|
||||
type: 'lookupRelated' | 'normal';
|
||||
propName: string;
|
||||
propValue: JsonObject;
|
||||
readOnly: boolean;
|
||||
};
|
||||
type LookupColumn = {
|
||||
type: 'lookup';
|
||||
propName: string;
|
||||
propValue: JsonObject;
|
||||
readOnly: boolean;
|
||||
schemaName: string;
|
||||
};
|
||||
/** Yields each property in a Dataverse entity's properties node, tagged by its role and mutability, for use in interface generation. */
|
||||
export declare function iterateDataverseProperties(propertiesNode: JsonObject, lookupColumns: Map<string, JsonObject>): Generator<GenericColumn | LookupColumn>;
|
||||
export {};
|
||||
//# sourceMappingURL=dataverseUtils.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dataverseUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/dataverseUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAOxD;AAED,+GAA+G;AAC/G,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAQtE;AAED,wFAAwF;AACxF,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAExE;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAQ7F;AAED,2HAA2H;AAC3H,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAOT;AAED,kHAAkH;AAClH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,8EAA8E;AAC9E,wBAAgB,+BAA+B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAKzE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAoBzE;AAED;;;GAGG;AACH,wBAAgB,sCAAsC,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAErF;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,CAAC,cAAc,EAAE,UAAU,GAAG,MAAM,EAAE,CAa1F;AAED,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAK/D;AAED,0GAA0G;AAC1G,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,uHAAuH;AACvH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAStD;AAED,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,eAAe,GAAG,QAAQ,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wIAAwI;AACxI,wBAAiB,0BAA0B,CACzC,cAAc,EAAE,UAAU,EAC1B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GACrC,SAAS,CAAC,aAAa,GAAG,YAAY,CAAC,CAezC"}
|
||||
CodeApp_NeonReactor/node_modules/@microsoft/power-apps-actions/dist/CodeGen/shared/dataverseUtils.js
Generated
Vendored
+154
@@ -0,0 +1,154 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { convertToValidIdentifier } from './nameUtils.js';
|
||||
import { isJsonObject } from './schemaUtils.js';
|
||||
/** Returns true if the schema root represents a Dataverse entity table. */
|
||||
export function isDataverseEntity(root) {
|
||||
return (isJsonObject(root) &&
|
||||
isJsonObject(root.schema) &&
|
||||
isJsonObject(root.schema.items) &&
|
||||
root.schema.items['x-ms-dataverse-entity'] === true);
|
||||
}
|
||||
/** Returns the Dataverse entity set name for a schema (used as the OData API path), or null if not defined. */
|
||||
export function getDataverseEntitySetName(root) {
|
||||
if (isJsonObject(root) && isJsonObject(root.schema) && isJsonObject(root.schema.items)) {
|
||||
const entitySetName = root.schema.items['x-ms-dataverse-entityset'];
|
||||
if (typeof entitySetName === 'string' && entitySetName.trim()) {
|
||||
return entitySetName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Returns true if the property is a Dataverse lookup relationship to another table. */
|
||||
export function isDataverseLookupProperty(propValue) {
|
||||
return propValue['x-ms-dataverse-type'] === 'LookupType';
|
||||
}
|
||||
/** Collects all lookup relationship columns from a properties node, keyed by property name. */
|
||||
export function getDataverseLookupColumns(propertiesNode) {
|
||||
const lookupColumns = new Map();
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
if (isJsonObject(propValue) && isDataverseLookupProperty(propValue)) {
|
||||
lookupColumns.set(propName, propValue);
|
||||
}
|
||||
}
|
||||
return lookupColumns;
|
||||
}
|
||||
/** Returns true if the property is an auto-generated name field for a lookup column (e.g. `owneridname` for `ownerid`). */
|
||||
export function isLookupRelatedProperty(propName, lookupColumns) {
|
||||
for (const lookupPropName of lookupColumns.keys()) {
|
||||
if (propName === `${lookupPropName}name`) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/** Returns the write-form property name for a lookup column, used when creating or updating Dataverse records. */
|
||||
export function generateODataBindPropertyName(schemaName) {
|
||||
return `${schemaName}@odata.bind`;
|
||||
}
|
||||
/** Returns the read-form property name for a lookup's resolved GUID value. */
|
||||
export function generateLookupValuePropertyName(lookupPropertyName) {
|
||||
return `_${lookupPropertyName}_value`;
|
||||
}
|
||||
/**
|
||||
* Returns true if the property is a Dataverse boolean (yes/no) field.
|
||||
* These fields have enum/x-ms-enum-values in the schema but must be typed as `boolean`,
|
||||
* not as a numeric optionset, because the Dataverse Web API expects true/false.
|
||||
*/
|
||||
export function isDataverseBooleanProperty(propValue) {
|
||||
return (propValue['x-ms-dataverse-type'] === 'BooleanType' ||
|
||||
propValue['x-ms-optionSetType'] === 'Boolean');
|
||||
}
|
||||
/**
|
||||
* Returns true if the property is a Dataverse numeric field (integer, decimal, double, money, bigint).
|
||||
* These fields have `"type": "string"` in the schema but must be typed as `number` because
|
||||
* the Dataverse Web API sends and receives them as JSON numbers.
|
||||
*/
|
||||
export function isDataverseNumericProperty(propValue) {
|
||||
const dvType = typeof propValue['x-ms-dataverse-type'] === 'string'
|
||||
? propValue['x-ms-dataverse-type'].toLowerCase()
|
||||
: undefined;
|
||||
switch (dvType) {
|
||||
case 'integer':
|
||||
case 'integertype':
|
||||
case 'bigint':
|
||||
case 'biginttype':
|
||||
case 'decimal':
|
||||
case 'decimaltype':
|
||||
case 'double':
|
||||
case 'doubletype':
|
||||
case 'money':
|
||||
case 'moneytype':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if the property is a Dataverse multi-select picklist (multi-choice) field.
|
||||
* These fields allow multiple selections and should be typed as an array of the enum type.
|
||||
*/
|
||||
export function isDataverseMultiSelectPicklistProperty(propValue) {
|
||||
return propValue['x-ms-dataverse-type'] === 'MultiSelectPicklistType';
|
||||
}
|
||||
/**
|
||||
* Returns the names of all MultiSelectPicklist (multi-choice) properties in a Dataverse entity
|
||||
* properties node — i.e., properties that have optionset data AND are typed as MultiSelectPicklistType.
|
||||
* These fields require serialization (array → comma-separated string) when writing to the Dataverse API
|
||||
* and deserialization (comma-separated string → array) when reading back from it.
|
||||
*/
|
||||
export function getDataverseMultiSelectPicklistFields(propertiesNode) {
|
||||
const fields = [];
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
if (isJsonObject(propValue) &&
|
||||
isDataverseMultiSelectPicklistProperty(propValue) &&
|
||||
Array.isArray(propValue.enum) &&
|
||||
propValue.enum.length > 0) {
|
||||
fields.push(propName);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
/** Returns true if the property represents an optionset (choice) column with enumerable values. */
|
||||
export function hasOptionsetData(propValue) {
|
||||
if (isDataverseBooleanProperty(propValue)) {
|
||||
return false;
|
||||
}
|
||||
return Array.isArray(propValue.enum) && propValue.enum.length > 0;
|
||||
}
|
||||
/** Produces the TypeScript identifier used for the enum or const generated from an optionset property. */
|
||||
export function generateOptionsetEnumName(modelName, propName) {
|
||||
return convertToValidIdentifier(`${modelName}${propName}`);
|
||||
}
|
||||
/** Formats an optionset display label as a TypeScript string literal for use in union types and const declarations. */
|
||||
export function formatEnumLabel(label) {
|
||||
if (label === null || label === undefined) {
|
||||
return "''";
|
||||
}
|
||||
const str = String(label);
|
||||
if (str.trim() === '') {
|
||||
return "''";
|
||||
}
|
||||
return `'${str.replace(/'/g, "\\'")}'`;
|
||||
}
|
||||
/** Yields each property in a Dataverse entity's properties node, tagged by its role and mutability, for use in interface generation. */
|
||||
export function* iterateDataverseProperties(propertiesNode, lookupColumns) {
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
if (isJsonObject(propValue)) {
|
||||
const readOnly = !!propValue['x-ms-read-only'];
|
||||
if (lookupColumns.has(propName)) {
|
||||
const schemaNameRaw = propValue['x-ms-schema-name'];
|
||||
const schemaName = typeof schemaNameRaw === 'string' ? schemaNameRaw : propName;
|
||||
yield { type: 'lookup', readOnly, propName, propValue, schemaName };
|
||||
}
|
||||
else if (isLookupRelatedProperty(propName, lookupColumns)) {
|
||||
yield { type: 'lookupRelated', readOnly, propName, propValue };
|
||||
}
|
||||
else {
|
||||
yield { type: 'normal', readOnly, propName, propValue };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=dataverseUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dataverseUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/dataverseUtils.ts"],"names":[],"mappings":"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"}
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import type { JsonObject } from '../../Common/jsonTypes.js';
|
||||
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
|
||||
/**
|
||||
* Derives the model name and data source name from a schema.
|
||||
* Handles Dataverse entities, shared connectors, GUIDs, SQL stored procedures, and generic connectors.
|
||||
*/
|
||||
export declare function extractModelName(schema: unknown, schemaPath: string, vfs: ReadOnlyVfs): {
|
||||
modelName: string;
|
||||
dataSourceName: string;
|
||||
};
|
||||
/**
|
||||
* Generates a compact single-line TypeScript object type for use inside `allOf` intersection types.
|
||||
* Returns a type expression like `{ id: string; name?: string; }`.
|
||||
*/
|
||||
export declare function generateCompactInlineObjectType(modelName: string, definition: JsonObject): string;
|
||||
/**
|
||||
* Generates an inline TypeScript object type string for a schema node that has nested `properties`.
|
||||
* Returns a type expression like `{ street?: string; city?: string; }` suitable for use as a
|
||||
* property type in both V1 string output and V2 ts-morph `type` fields.
|
||||
*/
|
||||
export declare function generateInlineObjectTypeString(modelName: string, definition: JsonObject, padding: string): string;
|
||||
/** Resolves the TypeScript type for a schema property, dispatching across optionsets, arrays, nested objects, refs, and primitives. */
|
||||
export declare function determineTypeScriptType(modelName: string, propValue: JsonObject | undefined, padding: string, propName?: string): string;
|
||||
//# sourceMappingURL=modelUtils.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"modelUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/modelUtils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA2BzD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,GACf;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAkC/C;AAqDD;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAmBjG;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,MAAM,GACd,MAAM,CAmBR;AAED,uIAAuI;AACvI,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,UAAU,GAAG,SAAS,EACjC,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAmCR"}
|
||||
Generated
Vendored
+185
@@ -0,0 +1,185 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { isGuid } from '@microsoft/power-apps-common/utils';
|
||||
import { generateOptionsetEnumName, getDataverseEntitySetName, hasOptionsetData, isDataverseBooleanProperty, isDataverseEntity, isDataverseMultiSelectPicklistProperty, isDataverseNumericProperty, } from './dataverseUtils.js';
|
||||
import { convertToValidIdentifier, formatPropertyName, normalizePropertyName, sanitizeName, sanitizeTabularResourceName, } from './nameUtils.js';
|
||||
import { extractReferencedType, getNameNode, getPropertiesNode, getRequiredFields, isJsonObject, mapJsonTypeToTypeScript, } from './schemaUtils.js';
|
||||
import { getParentFolderName, isSqlStoredProcedure } from './sqlUtils.js';
|
||||
/**
|
||||
* Derives the model name and data source name from a schema.
|
||||
* Handles Dataverse entities, shared connectors, GUIDs, SQL stored procedures, and generic connectors.
|
||||
*/
|
||||
export function extractModelName(schema, schemaPath, vfs) {
|
||||
if (!schema) {
|
||||
throw new Error('Schema cannot be null.');
|
||||
}
|
||||
// Check if this is a Dataverse entity schema
|
||||
if (isDataverseEntity(schema)) {
|
||||
const entitySetName = getDataverseEntitySetName(schema);
|
||||
if (entitySetName) {
|
||||
const modelName = capitalizeForFileName(sanitizeName(entitySetName));
|
||||
return { modelName, dataSourceName: entitySetName.trim() };
|
||||
}
|
||||
}
|
||||
const nameNode = getNameNode(schema);
|
||||
let modelName;
|
||||
let dataSourceName;
|
||||
if (isSharedName(nameNode)) {
|
||||
modelName = getDisplayName(schema);
|
||||
dataSourceName = getSharedDataSourceName(nameNode);
|
||||
}
|
||||
else if (isGuid(nameNode)) {
|
||||
modelName = getTitle(schema);
|
||||
dataSourceName = modelName;
|
||||
}
|
||||
else if (isSqlStoredProcedure(schema)) {
|
||||
modelName = sanitizeTabularResourceName(nameNode);
|
||||
dataSourceName = getParentFolderName(schemaPath, vfs);
|
||||
}
|
||||
else {
|
||||
modelName = sanitizeTabularResourceName(nameNode);
|
||||
dataSourceName = modelName;
|
||||
}
|
||||
modelName = capitalizeForFileName(sanitizeName(modelName));
|
||||
return { modelName, dataSourceName };
|
||||
}
|
||||
function capitalizeForFileName(name) {
|
||||
if (!name) {
|
||||
return name;
|
||||
}
|
||||
return name.charAt(0).toUpperCase() + name.slice(1);
|
||||
}
|
||||
function getDisplayName(schema) {
|
||||
if (isJsonObject(schema) && isJsonObject(schema.properties)) {
|
||||
const displayName = schema.properties.displayName;
|
||||
if (typeof displayName !== 'string' || !displayName.trim()) {
|
||||
throw new Error("No 'displayName' found in schema properties");
|
||||
}
|
||||
return displayName;
|
||||
}
|
||||
throw new Error('Schema must be a JSON object with properties to extract displayName');
|
||||
}
|
||||
function isSharedName(nameNode) {
|
||||
return nameNode.toLowerCase().startsWith('shared_');
|
||||
}
|
||||
function getSharedDataSourceName(nameNode) {
|
||||
return sanitizeName(nameNode.replace(/^shared_/i, '')).toLowerCase();
|
||||
}
|
||||
function getTitle(schema) {
|
||||
if (isJsonObject(schema)) {
|
||||
const title = schema.title;
|
||||
if (typeof title !== 'string' || !title.trim()) {
|
||||
throw new Error("No 'title' found in schema");
|
||||
}
|
||||
return title;
|
||||
}
|
||||
throw new Error('Schema must be a JSON object to extract title');
|
||||
}
|
||||
function addPropertyComment(lines, propValue, padding) {
|
||||
if (propValue?.description && typeof propValue.description === 'string') {
|
||||
// Split multi-line descriptions so every line is prefixed with '//'
|
||||
const commentLines = propValue.description
|
||||
.split(/\r?\n/)
|
||||
.map((line) => `${padding} // ${line}`);
|
||||
lines.push(...commentLines);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Generates a compact single-line TypeScript object type for use inside `allOf` intersection types.
|
||||
* Returns a type expression like `{ id: string; name?: string; }`.
|
||||
*/
|
||||
export function generateCompactInlineObjectType(modelName, definition) {
|
||||
const requiredFields = getRequiredFields(definition);
|
||||
const propertiesNode = getPropertiesNode(definition);
|
||||
const parts = [];
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
// Use formatPropertyName(propName) directly to preserve the original JSON key.
|
||||
// Do NOT pass through convertToValidIdentifier here — that's for identifier names
|
||||
// (type aliases, variable names), not property keys. e.g. "@odata.type" must stay
|
||||
// as `"@odata.type"` in the emitted interface to match the JSON wire format.
|
||||
let formattedPropName = formatPropertyName(propName);
|
||||
formattedPropName = normalizePropertyName(formattedPropName);
|
||||
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
|
||||
const tsType = determineTypeScriptType(modelName, propValueAsObject, '', propName);
|
||||
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
||||
parts.push(`${formattedPropName}${optionalMark}: ${tsType}`);
|
||||
}
|
||||
return `{ ${parts.join('; ')} }`;
|
||||
}
|
||||
/**
|
||||
* Generates an inline TypeScript object type string for a schema node that has nested `properties`.
|
||||
* Returns a type expression like `{ street?: string; city?: string; }` suitable for use as a
|
||||
* property type in both V1 string output and V2 ts-morph `type` fields.
|
||||
*/
|
||||
export function generateInlineObjectTypeString(modelName, definition, padding) {
|
||||
const requiredFields = getRequiredFields(definition);
|
||||
const propertiesNode = getPropertiesNode(definition);
|
||||
const lines = ['{'];
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
// Use formatPropertyName(propName) directly — same reason as generateCompactInlineObjectType:
|
||||
// property keys must match the JSON wire format, not be sanitized into identifier names.
|
||||
let formattedPropName = formatPropertyName(propName);
|
||||
formattedPropName = normalizePropertyName(formattedPropName);
|
||||
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
|
||||
const tsType = determineTypeScriptType(modelName, propValueAsObject, padding, propName);
|
||||
const optionalMark = requiredFields.has(propName) ? '' : '?';
|
||||
addPropertyComment(lines, propValueAsObject, padding);
|
||||
lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
|
||||
}
|
||||
lines.push(`${padding}}`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
/** Resolves the TypeScript type for a schema property, dispatching across optionsets, arrays, nested objects, refs, and primitives. */
|
||||
export function determineTypeScriptType(modelName, propValue, padding, propName) {
|
||||
// Dataverse boolean (yes/no) fields must be typed as `boolean`, not as a numeric optionset.
|
||||
// The Dataverse Web API expects true/false, not 0/1.
|
||||
if (propValue && isDataverseBooleanProperty(propValue)) {
|
||||
return 'boolean';
|
||||
}
|
||||
// Dataverse numeric fields (integer, decimal, double, money, bigint) have `"type": "string"`
|
||||
// in the schema but the Dataverse Web API sends/receives them as JSON numbers.
|
||||
if (propValue && isDataverseNumericProperty(propValue)) {
|
||||
return 'number';
|
||||
}
|
||||
// Check if this property has optionset data first
|
||||
if (propValue && propName && hasOptionsetData(propValue)) {
|
||||
const enumName = generateOptionsetEnumName(modelName, propName);
|
||||
// Multi-select picklists allow multiple selections, so they are typed as arrays.
|
||||
return isDataverseMultiSelectPicklistProperty(propValue) ? `${enumName}[]` : enumName;
|
||||
}
|
||||
if (propValue?.type === 'array' && isJsonObject(propValue.items)) {
|
||||
return determineArrayType(modelName, propValue.items, padding, propName);
|
||||
}
|
||||
else if (propValue?.properties && typeof propValue.properties === 'object') {
|
||||
// Use modelName + propName as the sub-model name so that nested enum type aliases
|
||||
// are unique even when two sibling inline objects have a property with the same name
|
||||
// (e.g. both `phoneNumbers.type` and `emailAddresses.type` → previously both got
|
||||
// `Createperson_Requesttype`, now they get `Createperson_RequestphoneNumberstype`
|
||||
// and `Createperson_RequestemailAddressestype`).
|
||||
const subModelName = propName ? `${modelName}${convertToValidIdentifier(propName)}` : modelName;
|
||||
return generateInlineObjectTypeString(subModelName, propValue, padding + ' ');
|
||||
}
|
||||
else if (typeof propValue?.$ref === 'string') {
|
||||
return extractReferencedType(propValue.$ref);
|
||||
}
|
||||
else {
|
||||
return mapJsonTypeToTypeScript(propValue?.type);
|
||||
}
|
||||
}
|
||||
/** Resolves the TypeScript element type for an array property. */
|
||||
function determineArrayType(modelName, itemsNode, padding, propName) {
|
||||
if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
|
||||
const subModelName = propName ? `${modelName}${convertToValidIdentifier(propName)}` : modelName;
|
||||
const inline = generateInlineObjectTypeString(subModelName, itemsNode, padding + ' ');
|
||||
return `${inline}[]`;
|
||||
}
|
||||
else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
|
||||
return `${extractReferencedType(itemsNode.$ref)}[]`;
|
||||
}
|
||||
else {
|
||||
const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
|
||||
return mapJsonTypeToTypeScript(itemType) + '[]';
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=modelUtils.js.map
|
||||
CodeApp_NeonReactor/node_modules/@microsoft/power-apps-actions/dist/CodeGen/shared/modelUtils.js.map
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
/** Converts an arbitrary string to a safe TypeScript identifier for use in generated code. */
|
||||
export declare function convertToValidIdentifier(variableOrInterfaceName: string): string;
|
||||
/** Sanitizes a raw string to a valid TypeScript identifier, for use when generating type and variable names. */
|
||||
export declare function sanitizeName(name: string, removeWhitespace?: boolean, prefixReservedKeywords?: boolean): string;
|
||||
/** Sanitizes a property name for use in a TypeScript interface, without prefixing reserved keywords. */
|
||||
export declare function sanitizePropertyName(name: string): string;
|
||||
/** Returns a unique sanitized name, appending a numeric suffix to avoid collisions with existing names. */
|
||||
export declare function getSanitizedUniqueName(existingNames: string[], desiredName: string): string;
|
||||
/** Normalizes a type name containing generic bracket syntax into a form suitable as a TypeScript identifier. */
|
||||
export declare function normalizeGenericTypeName(typeName: string): string;
|
||||
/**
|
||||
* Extracts a clean identifier from a potentially schema-qualified resource name,
|
||||
* for use in generated TypeScript identifiers.
|
||||
*
|
||||
* Handles bracket-qualified names used by SQL, Oracle, and other tabular connectors
|
||||
* (e.g. `[dbo].[TableName]` or `[OWNER].[TABLE_NAME]`), as well as plain names.
|
||||
* Always takes the last segment after the final dot, strips brackets, then sanitizes.
|
||||
*/
|
||||
export declare function sanitizeTabularResourceName(name: string): string;
|
||||
/** Returns the data source name accessor for the given model's service class. */
|
||||
export declare function getServiceName(modelName: string): string;
|
||||
/** Applies well-known casing fixes to property names before they are emitted into generated types. */
|
||||
export declare function normalizePropertyName(propertyName: string): string;
|
||||
/** Converts a kebab-case string to camelCase (e.g., `common-models` → `commonModels`). */
|
||||
export declare function kebabToCamelCase(name: string): string;
|
||||
/** Wraps a property name in quotes when it cannot appear as a bare TypeScript identifier. */
|
||||
export declare function formatPropertyName(propName: string): string;
|
||||
/** Returns true if a property name can be used without quotes in a TypeScript type definition. */
|
||||
export declare function isValidPropertyName(propName: string): boolean;
|
||||
//# sourceMappingURL=nameUtils.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"nameUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/nameUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkEH,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,uBAAuB,EAAE,MAAM,UAOvE;AAED,gHAAgH;AAChH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,GAAE,OAAc,EAChC,sBAAsB,GAAE,OAAc,GACrC,MAAM,CA8BR;AAED,wGAAwG;AACxG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,2GAA2G;AAC3G,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAsB3F;AAED,gHAAgH;AAChH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,sGAAsG;AACtG,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI7D"}
|
||||
Generated
Vendored
+180
@@ -0,0 +1,180 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
// TypeScript reserved keywords and built-in types
|
||||
const RESERVED_KEYWORDS = new Set([
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'enum',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'new',
|
||||
'null',
|
||||
'return',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'as',
|
||||
'implements',
|
||||
'interface',
|
||||
'let',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'static',
|
||||
'yield',
|
||||
'any',
|
||||
'boolean',
|
||||
'constructor',
|
||||
'declare',
|
||||
'get',
|
||||
'module',
|
||||
'require',
|
||||
'number',
|
||||
'set',
|
||||
'string',
|
||||
'symbol',
|
||||
'type',
|
||||
'from',
|
||||
'of',
|
||||
]);
|
||||
/** Converts an arbitrary string to a safe TypeScript identifier for use in generated code. */
|
||||
export function convertToValidIdentifier(variableOrInterfaceName) {
|
||||
if (variableOrInterfaceName.indexOf('@odata') > -1) {
|
||||
// Strip leading '@' and replace '.' with '_' to produce a valid identifier, e.g. "@odata.nextLink" -> "odata_nextLink"
|
||||
return variableOrInterfaceName.replace(/^@/, '').replace(/\./g, '_');
|
||||
}
|
||||
return sanitizeName(variableOrInterfaceName, false);
|
||||
}
|
||||
/** Sanitizes a raw string to a valid TypeScript identifier, for use when generating type and variable names. */
|
||||
export function sanitizeName(name, removeWhitespace = true, prefixReservedKeywords = true) {
|
||||
const originalName = name;
|
||||
// Remove any leading or trailing whitespace
|
||||
name = name.trim();
|
||||
if (!name) {
|
||||
throw new Error('Invalid identifier: empty string');
|
||||
}
|
||||
if (removeWhitespace) {
|
||||
name = name.replace(/\s/g, '');
|
||||
}
|
||||
// Remove any characters that are not alphanumeric or underscores
|
||||
name = name.replace(/[^a-zA-Z0-9_$]/g, '_');
|
||||
// Ensure the name does not start with a number
|
||||
if (/^\d/.test(name)) {
|
||||
name = `_${name}`;
|
||||
}
|
||||
// Ensure the name is not empty or all underscores
|
||||
if (name.length === 0 || /^_+$/.test(name)) {
|
||||
throw new Error(`Failed to sanitize string ${originalName}`);
|
||||
}
|
||||
if (prefixReservedKeywords && RESERVED_KEYWORDS.has(name)) {
|
||||
name = `_${name}`;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
/** Sanitizes a property name for use in a TypeScript interface, without prefixing reserved keywords. */
|
||||
export function sanitizePropertyName(name) {
|
||||
if (name.indexOf('@odata') > -1) {
|
||||
return `"${name}"`;
|
||||
}
|
||||
return sanitizeName(name, false, false);
|
||||
}
|
||||
/** Returns a unique sanitized name, appending a numeric suffix to avoid collisions with existing names. */
|
||||
export function getSanitizedUniqueName(existingNames, desiredName) {
|
||||
// Sanitize the base name
|
||||
desiredName = sanitizeName(desiredName);
|
||||
const nameSet = new Set(existingNames);
|
||||
if (!nameSet.has(desiredName)) {
|
||||
return desiredName.toLowerCase();
|
||||
}
|
||||
let maxSuffix = 0;
|
||||
// Non-literal regex shoud be safe here since baseName is sanitized
|
||||
// and should not contain any special characters that could lead to regex injection.
|
||||
const regex = new RegExp(`^${desiredName}_(\\d+)$`);
|
||||
for (const name of existingNames) {
|
||||
const match = name.match(regex);
|
||||
if (match) {
|
||||
const num = parseInt(match[1], 10);
|
||||
if (num > maxSuffix) {
|
||||
maxSuffix = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${desiredName}_${maxSuffix + 1}`.toLowerCase();
|
||||
}
|
||||
/** Normalizes a type name containing generic bracket syntax into a form suitable as a TypeScript identifier. */
|
||||
export function normalizeGenericTypeName(typeName) {
|
||||
return typeName.replace(/\[/g, '_').replace(/\]/g, '');
|
||||
}
|
||||
/**
|
||||
* Extracts a clean identifier from a potentially schema-qualified resource name,
|
||||
* for use in generated TypeScript identifiers.
|
||||
*
|
||||
* Handles bracket-qualified names used by SQL, Oracle, and other tabular connectors
|
||||
* (e.g. `[dbo].[TableName]` or `[OWNER].[TABLE_NAME]`), as well as plain names.
|
||||
* Always takes the last segment after the final dot, strips brackets, then sanitizes.
|
||||
*/
|
||||
export function sanitizeTabularResourceName(name) {
|
||||
const result = name.trim().replace(/\[|\]/g, '');
|
||||
const match = result.match(/\.?([^.]*)$/);
|
||||
if (match) {
|
||||
return sanitizeName(match[1]);
|
||||
}
|
||||
return sanitizeName(result);
|
||||
}
|
||||
/** Returns the data source name accessor for the given model's service class. */
|
||||
export function getServiceName(modelName) {
|
||||
if (!modelName || !modelName.trim()) {
|
||||
throw new Error('Model name cannot be null or empty.');
|
||||
}
|
||||
return `${modelName}Service.dataSourceName`;
|
||||
}
|
||||
/** Applies well-known casing fixes to property names before they are emitted into generated types. */
|
||||
export function normalizePropertyName(propertyName) {
|
||||
return propertyName.toLowerCase() === 'resultsets' ? 'ResultSets' : propertyName;
|
||||
}
|
||||
/** Converts a kebab-case string to camelCase (e.g., `common-models` → `commonModels`). */
|
||||
export function kebabToCamelCase(name) {
|
||||
return name.replace(/-+(.)/g, (_, c) => c.toUpperCase()).replace(/-+$/, '');
|
||||
}
|
||||
/** Wraps a property name in quotes when it cannot appear as a bare TypeScript identifier. */
|
||||
export function formatPropertyName(propName) {
|
||||
// Skip if already quoted
|
||||
if (propName.startsWith('"') && propName.endsWith('"')) {
|
||||
return propName;
|
||||
}
|
||||
return isValidPropertyName(propName) ? propName : `"${propName}"`;
|
||||
}
|
||||
/** Returns true if a property name can be used without quotes in a TypeScript type definition. */
|
||||
export function isValidPropertyName(propName) {
|
||||
// A valid property name must start with a letter, underscore, or dollar sign
|
||||
// and can only contain letters, numbers, underscores, or dollar signs
|
||||
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName);
|
||||
}
|
||||
//# sourceMappingURL=nameUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"nameUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/nameUtils.ts"],"names":[],"mappings":"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"}
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ILogger } from '../../services/index.js';
|
||||
import type { Vfs } from '../../Types/Vfs.types.js';
|
||||
/**
|
||||
* Compatibility layer for V1 and V2 codegen projects.
|
||||
*/
|
||||
export interface ICodegenProject {
|
||||
/** Create a file at the given path with the given content. */
|
||||
createFile(identifier: FileIdentifier, filePath: string, content: string): Promise<unknown>;
|
||||
/** Commit any uncommmited files to the filesystem */
|
||||
commit(): Promise<void>;
|
||||
/** Join path segments using the underlying path convention. */
|
||||
join(...paths: string[]): string;
|
||||
/** Returns true if a directory exists at the given path. */
|
||||
directoryExists(path: string): Promise<boolean>;
|
||||
/** Returns true if a file exists at the given path. */
|
||||
fileExists(path: string): Promise<boolean>;
|
||||
/** Returns the names of all source files directly inside the given directory. */
|
||||
getSourceFilesInDirectory(path: string): Promise<string[]>;
|
||||
}
|
||||
/**
|
||||
* The types of files that can be generated by codegen. Each project can have up to one of each.
|
||||
*/
|
||||
export type FileIdentifier = 'model' | 'service' | 'commonModel' | 'index';
|
||||
/**
|
||||
* Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
|
||||
* purely to preserve existing V1 codegen logic as closeley as possible.
|
||||
*/
|
||||
export declare class CodegenV1Project implements ICodegenProject {
|
||||
private vfs;
|
||||
private logger;
|
||||
constructor(vfs: Vfs, logger: ILogger);
|
||||
createFile(_identifier: FileIdentifier, filePath: string, content: string): Promise<void>;
|
||||
commit(): Promise<void>;
|
||||
join(...paths: string[]): string;
|
||||
directoryExists(path: string): Promise<boolean>;
|
||||
fileExists(path: string): Promise<boolean>;
|
||||
getSourceFilesInDirectory(path: string): Promise<string[]>;
|
||||
}
|
||||
//# sourceMappingURL=project.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,UAAU,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,qDAAqD;IACrD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,+DAA+D;IAC/D,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACjC,4DAA4D;IAC5D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,uDAAuD;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,iFAAiF;IACjF,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC;AAE3E;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,eAAe;IAEpD,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;gBADN,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,OAAO;IAGnB,UAAU,CAAC,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASzF,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAIhC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpC,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAGjE"}
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
/**
|
||||
* Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
|
||||
* purely to preserve existing V1 codegen logic as closeley as possible.
|
||||
*/
|
||||
export class CodegenV1Project {
|
||||
vfs;
|
||||
logger;
|
||||
constructor(vfs, logger) {
|
||||
this.vfs = vfs;
|
||||
this.logger = logger;
|
||||
}
|
||||
async createFile(_identifier, filePath, content) {
|
||||
// V1 codegen does not use the identifiers and relies in finding files by their path.
|
||||
const dir = this.vfs.dirname(filePath);
|
||||
if (!(await this.vfs.exists(dir))) {
|
||||
await this.vfs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
await this.vfs.writeFile(filePath, content);
|
||||
}
|
||||
async commit() {
|
||||
// No-op for V1 as files are written immediately
|
||||
}
|
||||
join(...paths) {
|
||||
return this.vfs.join(...paths);
|
||||
}
|
||||
directoryExists(path) {
|
||||
return this.vfs.exists(path);
|
||||
}
|
||||
fileExists(path) {
|
||||
return this.vfs.exists(path);
|
||||
}
|
||||
async getSourceFilesInDirectory(path) {
|
||||
return (await this.vfs.readdir(path)).filter((file) => file.endsWith('.ts'));
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=project.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4BH;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IAEjB;IACA;IAFV,YACU,GAAQ,EACR,MAAe;QADf,QAAG,GAAH,GAAG,CAAK;QACR,WAAM,GAAN,MAAM,CAAS;IACtB,CAAC;IAEJ,KAAK,CAAC,UAAU,CAAC,WAA2B,EAAE,QAAgB,EAAE,OAAe;QAC7E,qFAAqF;QACrF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,gDAAgD;IAClD,CAAC;IAED,IAAI,CAAC,GAAG,KAAe;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,IAAY;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,IAAY;QAC1C,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/E,CAAC;CACF"}
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { JsonObject } from '../../Common/jsonTypes.js';
|
||||
/**
|
||||
* Type guard for plain JSON objects. Used widely for safe property access
|
||||
* on `unknown` schema values before codegen processing begins.
|
||||
*/
|
||||
export declare function isJsonObject(value: unknown): value is JsonObject;
|
||||
/** Retrieves the properties map from a schema definition for iteration during code generation. */
|
||||
export declare function getPropertiesNode(definition: JsonObject): JsonObject;
|
||||
/** Returns the set of required property names from a schema, used to determine optionality in generated types. */
|
||||
export declare function getRequiredFields(definition: JsonObject): Set<string>;
|
||||
/** Converts a JSON Schema type to the TypeScript type used in generated interfaces. */
|
||||
export declare function mapJsonTypeToTypeScript(jsonType?: string): string;
|
||||
/** Resolves a JSON Schema `$ref` to the TypeScript type name it refers to. */
|
||||
export declare function extractReferencedType(refValue: string): string;
|
||||
/** Returns the schema's `name` field, which identifies the data source or connector. */
|
||||
export declare function getNameNode(schema: unknown): string;
|
||||
/** Recursively collects all `.json` file paths under the given directory. */
|
||||
export declare function getAllJsonFiles(folderPath: string): Promise<string[]>;
|
||||
//# sourceMappingURL=schemaUtils.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"schemaUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/schemaUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAGhE;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAKpE;AAED,kHAAkH;AAClH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAKrE;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAcjE;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CASnD;AAED,6EAA6E;AAC7E,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA6B3E"}
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { getVfs } from '../../VfsManager.js';
|
||||
import { convertToValidIdentifier, normalizeGenericTypeName } from './nameUtils.js';
|
||||
/**
|
||||
* Type guard for plain JSON objects. Used widely for safe property access
|
||||
* on `unknown` schema values before codegen processing begins.
|
||||
*/
|
||||
export function isJsonObject(value) {
|
||||
// Conclusive because 'object' type from JSON.parse() cannot have Symbol keys, or be undefined, functions, Dates, RegExp, Map/Set, or Classes
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
/** Retrieves the properties map from a schema definition for iteration during code generation. */
|
||||
export function getPropertiesNode(definition) {
|
||||
if (!('properties' in definition) || !isJsonObject(definition.properties)) {
|
||||
throw new Error("The definition does not contain a valid 'properties' node.");
|
||||
}
|
||||
return definition.properties;
|
||||
}
|
||||
/** Returns the set of required property names from a schema, used to determine optionality in generated types. */
|
||||
export function getRequiredFields(definition) {
|
||||
if ('required' in definition && Array.isArray(definition.required)) {
|
||||
return new Set(definition.required.map((r) => String(r)));
|
||||
}
|
||||
return new Set();
|
||||
}
|
||||
/** Converts a JSON Schema type to the TypeScript type used in generated interfaces. */
|
||||
export function mapJsonTypeToTypeScript(jsonType) {
|
||||
switch (jsonType) {
|
||||
case 'string':
|
||||
return 'string';
|
||||
case 'number':
|
||||
case 'integer':
|
||||
return 'number';
|
||||
case 'boolean':
|
||||
return 'boolean';
|
||||
case 'object':
|
||||
return 'Record<string, unknown>';
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
/** Resolves a JSON Schema `$ref` to the TypeScript type name it refers to. */
|
||||
export function extractReferencedType(refValue) {
|
||||
const parts = refValue.split('/');
|
||||
const raw = normalizeGenericTypeName(parts[parts.length - 1] ?? '');
|
||||
return convertToValidIdentifier(raw);
|
||||
}
|
||||
/** Returns the schema's `name` field, which identifies the data source or connector. */
|
||||
export function getNameNode(schema) {
|
||||
if (isJsonObject(schema)) {
|
||||
const name = schema.name;
|
||||
if (typeof name !== 'string' || !name.trim()) {
|
||||
throw new Error("No 'name' node found in schema");
|
||||
}
|
||||
return name.trim();
|
||||
}
|
||||
throw new Error('Schema must be a JSON object to extract name');
|
||||
}
|
||||
/** Recursively collects all `.json` file paths under the given directory. */
|
||||
export async function getAllJsonFiles(folderPath) {
|
||||
const files = [];
|
||||
const vfs = getVfs();
|
||||
async function recurse(dir) {
|
||||
try {
|
||||
const entries = await vfs.readdir(dir);
|
||||
for (const entry of entries) {
|
||||
const fullPath = vfs.join(dir, entry);
|
||||
if (entry.endsWith('.json')) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
else {
|
||||
// Check if it's a directory and recurse into it
|
||||
const isDir = await vfs.isDirectory(fullPath);
|
||||
if (isDir) {
|
||||
await recurse(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// If we can't read the directory, skip it silently
|
||||
// This could happen if it's not a directory or if permissions are insufficient
|
||||
}
|
||||
}
|
||||
await recurse(folderPath);
|
||||
return files;
|
||||
}
|
||||
//# sourceMappingURL=schemaUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"schemaUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/schemaUtils.ts"],"names":[],"mappings":"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"}
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
/** Returns true if the schema comes from a SharePoint data source. */
|
||||
export declare function isSharepointSchema(root: unknown): boolean;
|
||||
//# sourceMappingURL=sharepointUtils.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sharepointUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/sharepointUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAMzD"}
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { isJsonObject } from './schemaUtils.js';
|
||||
/** Returns true if the schema comes from a SharePoint data source. */
|
||||
export function isSharepointSchema(root) {
|
||||
// Logic to identify SharePoint schemas
|
||||
// Check if the schema has SharePoint-specific properties or structure
|
||||
return (isJsonObject(root) && !!root.referencedEntities && typeof root.referencedEntities === 'object');
|
||||
}
|
||||
//# sourceMappingURL=sharepointUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sharepointUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/sharepointUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,uCAAuC;IACvC,sEAAsE;IACtE,OAAO,CACL,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,IAAI,OAAO,IAAI,CAAC,kBAAkB,KAAK,QAAQ,CAC/F,CAAC;AACJ,CAAC"}
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
|
||||
/** Returns true if the schema represents a SQL stored procedure rather than a table. */
|
||||
export declare function isSqlStoredProcedure(root: unknown): boolean;
|
||||
/**
|
||||
* Returns the API key used to store a stored procedure in datasourcesinfo.
|
||||
* Strips brackets and the schema prefix (e.g. `[dbo].[testProc]` → `testProc`).
|
||||
* Must stay in sync with the equivalent logic in dataSourceInfoProcessor.ts.
|
||||
*/
|
||||
export declare function getSqlStoredProcApiKey(name: string): string;
|
||||
/** Returns the name of the folder containing a schema file, used as the data source name for SQL stored procedures. */
|
||||
export declare function getParentFolderName(schemaPath: string, vfs: ReadOnlyVfs): string;
|
||||
//# sourceMappingURL=sqlUtils.d.ts.map
|
||||
CodeApp_NeonReactor/node_modules/@microsoft/power-apps-actions/dist/CodeGen/shared/sqlUtils.d.ts.map
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sqlUtils.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/sqlUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAI3D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,uHAAuH;AACvH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAUhF"}
|
||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { isJsonObject } from './schemaUtils.js';
|
||||
/** Returns true if the schema represents a SQL stored procedure rather than a table. */
|
||||
export function isSqlStoredProcedure(root) {
|
||||
// Logic to identify SQL stored procedures
|
||||
// Check if the "schema" node contains a "procedureresultschema" node
|
||||
return isJsonObject(root) && isJsonObject(root.schema) && !!root.schema.procedureresultschema;
|
||||
}
|
||||
/**
|
||||
* Returns the API key used to store a stored procedure in datasourcesinfo.
|
||||
* Strips brackets and the schema prefix (e.g. `[dbo].[testProc]` → `testProc`).
|
||||
* Must stay in sync with the equivalent logic in dataSourceInfoProcessor.ts.
|
||||
*/
|
||||
export function getSqlStoredProcApiKey(name) {
|
||||
const withoutBrackets = name.replace(/\[|\]/g, '');
|
||||
return ('/' + withoutBrackets).replace(/^\/.+\./, '');
|
||||
}
|
||||
/** Returns the name of the folder containing a schema file, used as the data source name for SQL stored procedures. */
|
||||
export function getParentFolderName(schemaPath, vfs) {
|
||||
const directoryName = vfs.dirname(schemaPath);
|
||||
if (!directoryName) {
|
||||
throw new Error('The schemaPath does not have a valid directory.');
|
||||
}
|
||||
const parentFolderName = vfs.basename(directoryName);
|
||||
if (!parentFolderName) {
|
||||
throw new Error('The directory does not have a valid parent folder name.');
|
||||
}
|
||||
return parentFolderName.toLowerCase();
|
||||
}
|
||||
//# sourceMappingURL=sqlUtils.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sqlUtils.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/sqlUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,wFAAwF;AACxF,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,0CAA0C;IAC1C,qEAAqE;IACrE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;AAChG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,GAAG,eAAe,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,mBAAmB,CAAC,UAAkB,EAAE,GAAgB;IACtE,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,gBAAgB,CAAC,WAAW,EAAE,CAAC;AACxC,CAAC"}
|
||||
Generated
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ILogger } from '../../services/index.js';
|
||||
import type { ReadOnlyVfs } from '../../Types/Vfs.types.js';
|
||||
import type { CodegenV2Project } from '../v2/project.js';
|
||||
import type { ICodegenProject } from './project.js';
|
||||
interface ISharedContext {
|
||||
/** The folder containing the schema file being processed */
|
||||
schemaFolder: string;
|
||||
/** The path to the specific schema file being processed */
|
||||
schemaFile: string;
|
||||
/** The output folder where generated TypeScript files will be written */
|
||||
outputFolder: string;
|
||||
/** Logger for diagnostic messages */
|
||||
logger: ILogger;
|
||||
}
|
||||
/**
|
||||
* Vfs needed to read provided schema files.
|
||||
*/
|
||||
export interface IProcessingContext extends ISharedContext {
|
||||
/** Virtual file system for (only) reading schema files */
|
||||
vfs: ReadOnlyVfs;
|
||||
}
|
||||
/**
|
||||
* Contains additional context as a result of parsing schema.
|
||||
* Vfs is replaced by ICodegenProject for all IO.
|
||||
*/
|
||||
export interface ICodegenContext extends ISharedContext {
|
||||
/** Parsed JSON schema node */
|
||||
schema: unknown;
|
||||
/** The model name extracted from the schema (e.g., 'Account', 'Contact') */
|
||||
modelName: string;
|
||||
/** The data source name (e.g., 'Dataverse', 'Sharepoint', 'SqlServer') */
|
||||
dataSourceName: string;
|
||||
/** Codegen project for managing files */
|
||||
project: ICodegenProject;
|
||||
/** is V2 codegen enabled */
|
||||
isV2Enabled: boolean;
|
||||
}
|
||||
export interface ICodegenV2Context extends ICodegenContext {
|
||||
project: CodegenV2Project;
|
||||
isV2Enabled: true;
|
||||
}
|
||||
export declare function isCodegenV2Context(context: ICodegenContext): context is ICodegenV2Context;
|
||||
export {};
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/shared/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,UAAU,cAAc;IACtB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,0DAA0D;IAC1D,GAAG,EAAE,WAAW,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,4BAA4B;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,IAAI,iBAAiB,CAEzF"}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
export function isCodegenV2Context(context) {
|
||||
return context.isV2Enabled;
|
||||
}
|
||||
//# sourceMappingURL=types.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/CodeGen/shared/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgDH,MAAM,UAAU,kBAAkB,CAAC,OAAwB;IACzD,OAAO,OAAO,CAAC,WAAW,CAAC;AAC7B,CAAC"}
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { JsonObject } from '../../../Common/jsonTypes.js';
|
||||
import type { ICodegenV2Context } from '../../shared/types.js';
|
||||
interface GenerateModelContext {
|
||||
parentContext: ICodegenV2Context;
|
||||
schema: JsonObject;
|
||||
fileColumnNames: string[];
|
||||
imageColumnNames: string[];
|
||||
modelFilePath: string;
|
||||
}
|
||||
export declare function generateDataverseModel({ parentContext, schema, fileColumnNames, imageColumnNames, modelFilePath, }: GenerateModelContext): Promise<{
|
||||
hasLookupColumns: boolean;
|
||||
multiSelectPicklistFields: string[];
|
||||
}>;
|
||||
export default generateDataverseModel;
|
||||
//# sourceMappingURL=generateDataverseModel.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"generateDataverseModel.d.ts","sourceRoot":"","sources":["../../../../src/CodeGen/v2/Dataverse/generateDataverseModel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAoB5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,oBAAoB;IAC5B,aAAa,EAAE,iBAAiB,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,aAAa,EACb,MAAM,EACN,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,EAAE,oBAAoB,GAAG,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yBAAyB,EAAE,MAAM,EAAE,CAAC;CACrC,CAAC,CAsCD;AA6RD,eAAe,sBAAsB,CAAC"}
|
||||
Generated
Vendored
+227
@@ -0,0 +1,227 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { VariableDeclarationKind } from 'ts-morph';
|
||||
import { formatEnumLabel, generateLookupValuePropertyName, generateODataBindPropertyName, generateOptionsetEnumName, getDataverseLookupColumns, getDataverseMultiSelectPicklistFields, hasOptionsetData, iterateDataverseProperties, } from '../../shared/dataverseUtils.js';
|
||||
import { determineTypeScriptType } from '../../shared/modelUtils.js';
|
||||
import { convertToValidIdentifier, formatPropertyName, normalizePropertyName, sanitizeName, } from '../../shared/nameUtils.js';
|
||||
import { getPropertiesNode, getRequiredFields, isJsonObject } from '../../shared/schemaUtils.js';
|
||||
export async function generateDataverseModel({ parentContext, schema, fileColumnNames, imageColumnNames, modelFilePath, }) {
|
||||
const { modelName, project, logger } = parentContext;
|
||||
const sourceFile = await project.createFile('model', modelFilePath, '');
|
||||
const propertiesNode = getPropertiesNode(schema);
|
||||
const lookupColumns = getDataverseLookupColumns(propertiesNode);
|
||||
const hasLookupColumns = lookupColumns.size > 0;
|
||||
const multiSelectPicklistFields = getDataverseMultiSelectPicklistFields(propertiesNode);
|
||||
addOptionsetDeclarations(sourceFile, modelName, propertiesNode);
|
||||
if (!hasLookupColumns) {
|
||||
addFullInterface(sourceFile, modelName, schema, propertiesNode);
|
||||
}
|
||||
else {
|
||||
const baseInterfaceName = `${modelName}Base`;
|
||||
addBaseInterface(sourceFile, baseInterfaceName, modelName, schema, lookupColumns, propertiesNode, logger);
|
||||
addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode);
|
||||
}
|
||||
addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames);
|
||||
return { hasLookupColumns, multiSelectPicklistFields };
|
||||
}
|
||||
// ─── Upload Column Name Type ──────────────────────────────────────────────────
|
||||
function addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames) {
|
||||
const id = convertToValidIdentifier(modelName);
|
||||
if (fileColumnNames.length > 0) {
|
||||
sourceFile.addTypeAlias({
|
||||
name: `${id}FileColumnName`,
|
||||
isExported: true,
|
||||
type: fileColumnNames.map((col) => `'${col}'`).join(' | '),
|
||||
});
|
||||
}
|
||||
if (imageColumnNames.length > 0) {
|
||||
sourceFile.addTypeAlias({
|
||||
name: `${id}ImageColumnName`,
|
||||
isExported: true,
|
||||
type: imageColumnNames.map((col) => `'${col}'`).join(' | '),
|
||||
});
|
||||
}
|
||||
if (fileColumnNames.length === 0 && imageColumnNames.length === 0) {
|
||||
return;
|
||||
}
|
||||
const uploadType = fileColumnNames.length > 0 && imageColumnNames.length > 0
|
||||
? `${id}FileColumnName | ${id}ImageColumnName`
|
||||
: fileColumnNames.length > 0
|
||||
? `${id}FileColumnName`
|
||||
: `${id}ImageColumnName`;
|
||||
sourceFile.addTypeAlias({
|
||||
name: `${id}UploadColumnName`,
|
||||
isExported: true,
|
||||
type: uploadType,
|
||||
});
|
||||
}
|
||||
// ─── Optionset Declarations ───────────────────────────────────────────────────
|
||||
function addOptionsetDeclarations(sourceFile, modelName, propertiesNode) {
|
||||
for (const [propName, propValue] of Object.entries(propertiesNode)) {
|
||||
if (!isJsonObject(propValue) || !hasOptionsetData(propValue)) {
|
||||
continue;
|
||||
}
|
||||
const enumName = generateOptionsetEnumName(modelName, propName);
|
||||
const enumValues = propValue['x-ms-enum-values'];
|
||||
const enumLabels = propValue.enum;
|
||||
if (!Array.isArray(enumValues)) {
|
||||
// No x-ms-enum-values: emit a string union type alias from the enum labels.
|
||||
sourceFile.addTypeAlias({
|
||||
name: enumName,
|
||||
isExported: true,
|
||||
type: enumLabels.map(formatEnumLabel).join(' | '),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (!enumValues.every((val) => typeof val === 'number')) {
|
||||
throw new Error(`Unsupported Dataverse optionset format: x-ms-enum-values must be an array of numbers. Property: ${propName} in model ${modelName}`);
|
||||
}
|
||||
// Dataverse format: x-ms-enum-values contains numeric values.
|
||||
// Emit a const object mapping numeric keys to display names, plus a keyof type alias.
|
||||
const entries = enumLabels.map((label, i) => {
|
||||
const sanitizedLabel = !label || (typeof label === 'string' && label.trim() === '')
|
||||
? `EmptyOption${i}`
|
||||
: sanitizeName(label);
|
||||
return ` ${enumValues[i]}: '${sanitizedLabel}'`;
|
||||
});
|
||||
sourceFile.addVariableStatement({
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
isExported: true,
|
||||
declarations: [
|
||||
{
|
||||
name: enumName,
|
||||
initializer: `{\n${entries.join(',\n')}\n} as const`,
|
||||
},
|
||||
],
|
||||
});
|
||||
sourceFile.addTypeAlias({
|
||||
name: enumName,
|
||||
isExported: true,
|
||||
type: `keyof typeof ${enumName}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds a single interface for entities with no lookup columns.
|
||||
* All properties (including read-only) are included.
|
||||
*/
|
||||
function addFullInterface(sourceFile, modelName, schema, //DataverseEntitySchema,
|
||||
propertiesNode //DataverseEntitySchema['properties']
|
||||
) {
|
||||
const requiredFields = getRequiredFields(schema);
|
||||
const properties = Object.entries(propertiesNode).map(([propName, propValue]) => {
|
||||
const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
|
||||
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
|
||||
const description = typeof propValueAsObject?.description === 'string'
|
||||
? propValueAsObject.description
|
||||
: undefined;
|
||||
return {
|
||||
name: formattedName,
|
||||
type: determineTypeScriptType(modelName, propValueAsObject, '', propName),
|
||||
hasQuestionToken: !requiredFields.has(propName),
|
||||
docs: description ? [{ description }] : undefined,
|
||||
};
|
||||
});
|
||||
sourceFile.addInterface({
|
||||
name: convertToValidIdentifier(modelName),
|
||||
isExported: true,
|
||||
properties,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds the base interface for PATCH/POST operations on entities with lookup columns.
|
||||
* - Skips read-only properties (they belong in the extended GET interface)
|
||||
* - Replaces lookup columns with @odata.bind string properties
|
||||
* - Skips lookup-related name properties (e.g., owneridname)
|
||||
*/
|
||||
function addBaseInterface(sourceFile, interfaceName, originalModelName, schema, lookupColumns, propertiesNode, logger) {
|
||||
const properties = [];
|
||||
const requiredFields = getRequiredFields(schema);
|
||||
for (const prop of iterateDataverseProperties(propertiesNode, lookupColumns)) {
|
||||
const { type, readOnly, propName, propValue } = prop;
|
||||
// lookupRelated properties (e.g., owneridname) should always be read-only.
|
||||
// Log a warning if an anomaly is detected, but it will be skipped regardless.
|
||||
if (logger && type === 'lookupRelated' && !readOnly) {
|
||||
logger.trackActivityEvent('CodegenV2.generateDataverseModel.addBaseInterface.warning', {
|
||||
message: 'Anomaly: lookupRelated property is not read-only',
|
||||
modelName: originalModelName,
|
||||
columnName: propName,
|
||||
});
|
||||
}
|
||||
// Skip read-only properties and lookup-related properties (e.g., owneridname)
|
||||
// They are handled in the extended interface, and cannot be written to.
|
||||
if (readOnly || type === 'lookupRelated') {
|
||||
continue;
|
||||
}
|
||||
// Replace the lookup columns with an @odata.bind property for write operations.
|
||||
if (type === 'lookup') {
|
||||
properties.push({
|
||||
name: formatPropertyName(generateODataBindPropertyName(prop.schemaName)),
|
||||
type: 'string',
|
||||
hasQuestionToken: !requiredFields.has(propName),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
|
||||
const description = typeof propValue.description === 'string' ? propValue.description : undefined;
|
||||
properties.push({
|
||||
name: formattedName,
|
||||
type: determineTypeScriptType(originalModelName, propValue, '', propName),
|
||||
hasQuestionToken: !requiredFields.has(propName),
|
||||
docs: description ? [{ description }] : undefined,
|
||||
});
|
||||
}
|
||||
sourceFile.addInterface({
|
||||
name: convertToValidIdentifier(interfaceName),
|
||||
isExported: true,
|
||||
properties,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds the extended interface for GET operations on entities with lookup columns.
|
||||
* Extends the base interface and adds:
|
||||
* - Read-only properties (excluding lookup columns, which are handled separately)
|
||||
* - Lookup object properties (e.g., ownerid?: object)
|
||||
* - Lookup value properties (e.g., _ownerid_value?: string)
|
||||
*/
|
||||
function addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode) {
|
||||
const requiredFields = getRequiredFields(schema);
|
||||
const properties = [];
|
||||
for (const { type, readOnly, propName, propValue } of iterateDataverseProperties(propertiesNode, lookupColumns)) {
|
||||
// Exclude non-readonly properties (they are handled in the base interface)
|
||||
// Exclude lookup properties, they are handled separately below
|
||||
if (!readOnly || type === 'lookup') {
|
||||
continue;
|
||||
}
|
||||
const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
|
||||
const description = typeof propValue.description === 'string' ? propValue.description : undefined;
|
||||
properties.push({
|
||||
name: formattedName,
|
||||
type: determineTypeScriptType(modelName, propValue, '', propName),
|
||||
hasQuestionToken: !requiredFields.has(propName),
|
||||
docs: description ? [{ description }] : undefined,
|
||||
});
|
||||
}
|
||||
// Lookup-specific properties: the lookup object and its extracted GUID value.
|
||||
for (const lookupPropName of lookupColumns.keys()) {
|
||||
properties.push({
|
||||
name: formatPropertyName(convertToValidIdentifier(lookupPropName)),
|
||||
type: 'object',
|
||||
hasQuestionToken: true,
|
||||
});
|
||||
properties.push({
|
||||
name: formatPropertyName(generateLookupValuePropertyName(lookupPropName)),
|
||||
type: 'string',
|
||||
hasQuestionToken: true,
|
||||
});
|
||||
}
|
||||
sourceFile.addInterface({
|
||||
name: convertToValidIdentifier(modelName),
|
||||
isExported: true,
|
||||
extends: [convertToValidIdentifier(baseInterfaceName)],
|
||||
properties,
|
||||
});
|
||||
}
|
||||
export default generateDataverseModel;
|
||||
//# sourceMappingURL=generateDataverseModel.js.map
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type { ICodegenV2Context } from '../../shared/types.js';
|
||||
interface GenerateServiceContext {
|
||||
parentContext: ICodegenV2Context;
|
||||
primaryKey: string;
|
||||
hasLookupColumns: boolean;
|
||||
multiSelectPicklistFields: string[];
|
||||
fileColumnNames: string[];
|
||||
imageColumnNames: string[];
|
||||
serviceFilePath: string;
|
||||
dataSourceInfoPath: string;
|
||||
}
|
||||
export declare function generateDataverseService({ parentContext, primaryKey, hasLookupColumns, multiSelectPicklistFields, fileColumnNames, imageColumnNames, serviceFilePath, dataSourceInfoPath, }: GenerateServiceContext): Promise<void>;
|
||||
export default generateDataverseService;
|
||||
//# sourceMappingURL=generateDataverseService.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"generateDataverseService.d.ts","sourceRoot":"","sources":["../../../../src/CodeGen/v2/Dataverse/generateDataverseService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,UAAU,sBAAsB;IAC9B,aAAa,EAAE,iBAAiB,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyUxC;AAED,eAAe,wBAAwB,CAAC"}
|
||||
Generated
Vendored
+323
@@ -0,0 +1,323 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { Scope } from 'ts-morph';
|
||||
import { convertToValidIdentifier } from '../../shared/nameUtils.js';
|
||||
export async function generateDataverseService({ parentContext, primaryKey, hasLookupColumns, multiSelectPicklistFields, fileColumnNames, imageColumnNames, serviceFilePath, dataSourceInfoPath, }) {
|
||||
const { modelName, dataSourceName, project } = parentContext;
|
||||
const serviceName = `${convertToValidIdentifier(modelName)}Service`;
|
||||
// When there are lookup columns the model generator emits a separate ModelNameBase interface
|
||||
// for write operations (create/update) and ModelName extends it for read operations.
|
||||
// When there are none, a single ModelName interface covers all operations.
|
||||
const writeTypeName = hasLookupColumns
|
||||
? `${convertToValidIdentifier(modelName)}Base`
|
||||
: convertToValidIdentifier(modelName);
|
||||
const readTypeName = convertToValidIdentifier(modelName);
|
||||
const id = convertToValidIdentifier(modelName);
|
||||
const fileColumnNameType = `${id}FileColumnName`;
|
||||
const imageColumnNameType = `${id}ImageColumnName`;
|
||||
const uploadColumnNameType = `${id}UploadColumnName`;
|
||||
const hasFileColumns = fileColumnNames.length > 0;
|
||||
const hasImageColumns = imageColumnNames.length > 0;
|
||||
const hasFileOrImageColumns = hasFileColumns || hasImageColumns;
|
||||
const hasMultiSelectFields = multiSelectPicklistFields.length > 0;
|
||||
const createParamType = primaryKey !== '' ? `Omit<${writeTypeName}, '${primaryKey}'>` : writeTypeName;
|
||||
const updateParamType = primaryKey !== '' ? `Partial<Omit<${writeTypeName}, '${primaryKey}'>>` : writeTypeName;
|
||||
const sourceFile = await project.createFile('service', serviceFilePath, '');
|
||||
// External package imports — fixMissingImports cannot resolve these.
|
||||
const dataValueImports = ['getClient'];
|
||||
if (hasMultiSelectFields) {
|
||||
dataValueImports.push('deserializeMultiSelectPicklistFields', 'serializeMultiSelectPicklistFields');
|
||||
}
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@microsoft/power-apps/data',
|
||||
namedImports: dataValueImports,
|
||||
});
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@microsoft/power-apps/data',
|
||||
namedImports: ['IOperationResult'],
|
||||
isTypeOnly: true,
|
||||
});
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@microsoft/power-apps/data/metadata/dataverse',
|
||||
namedImports: ['EntityMetadata', 'GetEntityMetadataOptions'],
|
||||
isTypeOnly: true,
|
||||
});
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: dataSourceInfoPath,
|
||||
namedImports: ['dataSourcesInfo'],
|
||||
});
|
||||
// Internal in-project type imports - fixMissingImports would auto-add these at commit time,
|
||||
// but it cannot detect that these are type-only imports. We add them explicitly to make them type-only.
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: sourceFile.getRelativePathAsModuleSpecifierTo(project.getSourceFile('commonModel')),
|
||||
namedImports: ['IGetAllOptions', 'IGetOptions'],
|
||||
isTypeOnly: true,
|
||||
});
|
||||
const modelNamedImports = hasLookupColumns ? [writeTypeName, readTypeName] : [readTypeName];
|
||||
if (hasFileColumns) {
|
||||
modelNamedImports.push(fileColumnNameType);
|
||||
}
|
||||
if (hasImageColumns) {
|
||||
modelNamedImports.push(imageColumnNameType);
|
||||
}
|
||||
if (hasFileOrImageColumns) {
|
||||
modelNamedImports.push(uploadColumnNameType);
|
||||
}
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: sourceFile.getRelativePathAsModuleSpecifierTo(project.getSourceFile('model')),
|
||||
namedImports: modelNamedImports,
|
||||
isTypeOnly: true,
|
||||
});
|
||||
const multiSelectFieldsInitializer = `[${multiSelectPicklistFields.map((f) => `'${f}'`).join(', ')}] as const`;
|
||||
sourceFile.addClass({
|
||||
name: serviceName,
|
||||
isExported: true,
|
||||
properties: [
|
||||
{
|
||||
name: 'dataSourceName',
|
||||
isStatic: true,
|
||||
isReadonly: true,
|
||||
scope: Scope.Private,
|
||||
initializer: `'${dataSourceName.toLowerCase()}'`,
|
||||
},
|
||||
{
|
||||
name: 'client',
|
||||
isStatic: true,
|
||||
isReadonly: true,
|
||||
scope: Scope.Private,
|
||||
initializer: 'getClient(dataSourcesInfo)',
|
||||
},
|
||||
// Emitted only when the entity has MultiSelectPicklist columns. Used as the field list
|
||||
// passed to serializeMultiSelectPicklistFields / deserializeMultiSelectPicklistFields.
|
||||
...(hasMultiSelectFields
|
||||
? [
|
||||
{
|
||||
name: 'multiSelectPicklistFields',
|
||||
isStatic: true,
|
||||
isReadonly: true,
|
||||
scope: Scope.Private,
|
||||
initializer: multiSelectFieldsInitializer,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
methods: [
|
||||
{
|
||||
name: 'create',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [{ name: 'record', type: createParamType }],
|
||||
returnType: `Promise<IOperationResult<${readTypeName}>>`,
|
||||
statements: hasMultiSelectFields
|
||||
? [
|
||||
`const result = await ${serviceName}.client.createRecordAsync<Record<string, unknown>, ${readTypeName}>(${serviceName}.dataSourceName, serializeMultiSelectPicklistFields(record as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
|
||||
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
|
||||
'return result;',
|
||||
]
|
||||
: [
|
||||
`const result = await ${serviceName}.client.createRecordAsync<${createParamType}, ${readTypeName}>(${serviceName}.dataSourceName, record);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'update',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'changedFields', type: updateParamType },
|
||||
],
|
||||
returnType: `Promise<IOperationResult<${readTypeName}>>`,
|
||||
statements: hasMultiSelectFields
|
||||
? [
|
||||
`const result = await ${serviceName}.client.updateRecordAsync<Record<string, unknown>, ${readTypeName}>(${serviceName}.dataSourceName, id, serializeMultiSelectPicklistFields(changedFields as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
|
||||
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
|
||||
'return result;',
|
||||
]
|
||||
: [
|
||||
`const result = await ${serviceName}.client.updateRecordAsync<${updateParamType}, ${readTypeName}>(${serviceName}.dataSourceName, id, changedFields);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [{ name: 'id', type: 'string' }],
|
||||
returnType: 'Promise<void>',
|
||||
statements: [
|
||||
`await ${serviceName}.client.deleteRecordAsync(${serviceName}.dataSourceName, id);`,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'get',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'options', type: 'IGetOptions', hasQuestionToken: true },
|
||||
],
|
||||
returnType: `Promise<IOperationResult<${readTypeName}>>`,
|
||||
statements: hasMultiSelectFields
|
||||
? [
|
||||
`const result = await ${serviceName}.client.retrieveRecordAsync<${readTypeName}>(${serviceName}.dataSourceName, id, options);`,
|
||||
`if (result.data) { deserializeMultiSelectPicklistFields(result.data as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields); }`,
|
||||
'return result;',
|
||||
]
|
||||
: [
|
||||
`const result = await ${serviceName}.client.retrieveRecordAsync<${readTypeName}>(${serviceName}.dataSourceName, id, options);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'getAll',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [{ name: 'options', type: 'IGetAllOptions', hasQuestionToken: true }],
|
||||
returnType: `Promise<IOperationResult<${readTypeName}[]>>`,
|
||||
statements: hasMultiSelectFields
|
||||
? [
|
||||
`const result = await ${serviceName}.client.retrieveMultipleRecordsAsync<${readTypeName}>(${serviceName}.dataSourceName, options);`,
|
||||
`result.data?.forEach(record => deserializeMultiSelectPicklistFields(record as Record<string, unknown>, ${serviceName}.multiSelectPicklistFields));`,
|
||||
'return result;',
|
||||
]
|
||||
: [
|
||||
`const result = await ${serviceName}.client.retrieveMultipleRecordsAsync<${readTypeName}>(${serviceName}.dataSourceName, options);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'getMetadata',
|
||||
isStatic: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{
|
||||
name: 'options',
|
||||
type: `GetEntityMetadataOptions<${readTypeName}>`,
|
||||
initializer: '{}',
|
||||
},
|
||||
],
|
||||
returnType: `Promise<IOperationResult<Partial<EntityMetadata>>>`,
|
||||
statements: [
|
||||
`return ${serviceName}.client.executeAsync({`,
|
||||
` dataverseRequest: {`,
|
||||
` action: 'getEntityMetadata',`,
|
||||
` parameters: {`,
|
||||
` tableName: ${serviceName}.dataSourceName,`,
|
||||
` options: options as GetEntityMetadataOptions,`,
|
||||
` },`,
|
||||
` },`,
|
||||
`});`,
|
||||
],
|
||||
},
|
||||
...(hasFileOrImageColumns
|
||||
? [
|
||||
{
|
||||
name: 'upload',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'columnName', type: uploadColumnNameType },
|
||||
{ name: 'file', type: 'File' },
|
||||
{ name: 'fileDisplayName', type: 'string', hasQuestionToken: true },
|
||||
],
|
||||
returnType: 'Promise<IOperationResult<void>>',
|
||||
statements: [
|
||||
'const arrayBuffer = await file.arrayBuffer();',
|
||||
'const data = new Uint8Array(arrayBuffer);',
|
||||
`const result = await ${serviceName}.client.uploadFileToRecord(`,
|
||||
` ${serviceName}.dataSourceName,`,
|
||||
` id,`,
|
||||
` columnName,`,
|
||||
` fileDisplayName || file.name,`,
|
||||
` data,`,
|
||||
`);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasFileColumns
|
||||
? [
|
||||
{
|
||||
name: 'downloadFile',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'columnName', type: fileColumnNameType },
|
||||
],
|
||||
returnType: 'Promise<IOperationResult<Uint8Array>>',
|
||||
statements: [
|
||||
`const result = await ${serviceName}.client.downloadFileFromRecord(`,
|
||||
` ${serviceName}.dataSourceName,`,
|
||||
` id,`,
|
||||
` columnName,`,
|
||||
`);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasImageColumns
|
||||
? [
|
||||
{
|
||||
name: 'downloadImage',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'columnName', type: imageColumnNameType },
|
||||
{ name: 'fullSize', type: 'boolean', initializer: 'false' },
|
||||
],
|
||||
returnType: 'Promise<IOperationResult<Uint8Array>>',
|
||||
statements: [
|
||||
`const result = await ${serviceName}.client.downloadImageFromRecord(`,
|
||||
` ${serviceName}.dataSourceName,`,
|
||||
` id,`,
|
||||
` columnName,`,
|
||||
` fullSize,`,
|
||||
`);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(hasFileOrImageColumns
|
||||
? [
|
||||
{
|
||||
name: 'deleteFileOrImage',
|
||||
isStatic: true,
|
||||
isAsync: true,
|
||||
scope: Scope.Public,
|
||||
parameters: [
|
||||
{ name: 'id', type: 'string' },
|
||||
{ name: 'columnName', type: uploadColumnNameType },
|
||||
],
|
||||
returnType: 'Promise<IOperationResult<void>>',
|
||||
statements: [
|
||||
`const result = await ${serviceName}.client.deleteFileOrImageFromRecord(`,
|
||||
` ${serviceName}.dataSourceName,`,
|
||||
` id,`,
|
||||
` columnName,`,
|
||||
`);`,
|
||||
'return result;',
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
});
|
||||
}
|
||||
export default generateDataverseService;
|
||||
//# sourceMappingURL=generateDataverseService.js.map
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import type prettier from 'prettier';
|
||||
export declare const defaultPrettierConfig: prettier.Options;
|
||||
//# sourceMappingURL=defaultPrettierConfig.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"defaultPrettierConfig.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/v2/defaultPrettierConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAErC,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,OAK5C,CAAC"}
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
export const defaultPrettierConfig = {
|
||||
endOfLine: 'auto',
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
};
|
||||
//# sourceMappingURL=defaultPrettierConfig.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"defaultPrettierConfig.js","sourceRoot":"","sources":["../../../src/CodeGen/v2/defaultPrettierConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACrD,SAAS,EAAE,MAAM;IACjB,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,IAAI;IACjB,aAAa,EAAE,KAAK;CACrB,CAAC"}
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import { type CompilerOptions, type SourceFile } from 'ts-morph';
|
||||
import type { ILogger, Scenario } from '../../services/index.js';
|
||||
import type { Vfs } from '../../Types/Vfs.types.js';
|
||||
import type { FileIdentifier, ICodegenProject } from '../shared/project.js';
|
||||
export declare class CodegenV2Project implements ICodegenProject {
|
||||
private vfs;
|
||||
private logger;
|
||||
private tsMorphProject;
|
||||
private fileMap;
|
||||
constructor(vfs: Vfs, logger: ILogger, compilerOptions?: CompilerOptions);
|
||||
createFile(identifier: FileIdentifier, filePath: string, content: string): Promise<SourceFile>;
|
||||
commit(): Promise<void>;
|
||||
join(...paths: string[]): string;
|
||||
fileExists(path: string): Promise<boolean>;
|
||||
directoryExists(path: string): Promise<boolean>;
|
||||
getSourceFilesInDirectory(path: string): Promise<string[]>;
|
||||
getSourceFile(identifier: FileIdentifier): SourceFile;
|
||||
normalizeFile(sourceFile: SourceFile, scenario?: Scenario): Promise<{
|
||||
content: string;
|
||||
path: string;
|
||||
}>;
|
||||
}
|
||||
//# sourceMappingURL=project.d.ts.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/CodeGen/v2/project.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,KAAK,eAAe,EAAW,KAAK,UAAU,EAAM,MAAM,UAAU,CAAC;AAE9E,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAqBzE,qBAAa,gBAAiB,YAAW,eAAe;IAKpD,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,OAAO,CAAkC;gBAGvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,OAAO,EACvB,eAAe,CAAC,EAAE,eAAe;IAiB7B,UAAU,CACd,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,CAAC;IAahB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC7B,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAI1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1C,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/C,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQhE,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,UAAU;IAU/C,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAwC9C"}
|
||||
Generated
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
/*!
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
import prettier from 'prettier';
|
||||
import { Project, ts } from 'ts-morph';
|
||||
import { defaultPrettierConfig } from './defaultPrettierConfig.js';
|
||||
const defaultCompilerOptions = {
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
};
|
||||
const COPYRIGHT_BANNER = [
|
||||
'/*!',
|
||||
' * Copyright (C) Microsoft Corporation. All rights reserved.',
|
||||
' * This file is autogenerated. Do not edit this file directly.',
|
||||
' */',
|
||||
'',
|
||||
'',
|
||||
].join('\n');
|
||||
/*
|
||||
Compatibility layer for V1 and V2 codegen projects. The purpose of this class is
|
||||
purely to allow V1 modelServiceGenerator to use ts-morph backend without modification.
|
||||
*/
|
||||
export class CodegenV2Project {
|
||||
vfs;
|
||||
logger;
|
||||
tsMorphProject;
|
||||
fileMap;
|
||||
constructor(vfs, logger, compilerOptions) {
|
||||
this.vfs = vfs;
|
||||
this.logger = logger;
|
||||
const _compilerOptions = compilerOptions ?? defaultCompilerOptions;
|
||||
this.tsMorphProject = new Project({
|
||||
useInMemoryFileSystem: true,
|
||||
compilerOptions: _compilerOptions,
|
||||
});
|
||||
this.logger?.trackActivityEvent('Codegenv2.project.constructor', {
|
||||
message: 'Project created',
|
||||
compilerOptions: _compilerOptions,
|
||||
});
|
||||
this.fileMap = new Map();
|
||||
}
|
||||
async createFile(identifier, filePath, content) {
|
||||
const sourceFile = this.tsMorphProject.createSourceFile(filePath, content, { overwrite: true });
|
||||
if (this.fileMap.has(identifier)) {
|
||||
this.logger?.trackActivityEvent('Codegenv2.project.createFile.duplicateIdentifier', {
|
||||
message: `File with identifier ${identifier} already exists. Overwriting previous file.`,
|
||||
identifier,
|
||||
file: sourceFile.getBaseName(),
|
||||
});
|
||||
}
|
||||
this.fileMap.set(identifier, sourceFile);
|
||||
return sourceFile;
|
||||
}
|
||||
async commit() {
|
||||
const scenario = this.logger?.trackScenario('Codegenv2.project.commit');
|
||||
const normalizedContent = await Promise.all(this.tsMorphProject
|
||||
.getSourceFiles()
|
||||
.map((sourceFile) => this.normalizeFile(sourceFile, scenario)));
|
||||
// Write all to Vfs (stops on first error)
|
||||
let writtenCount = 0;
|
||||
try {
|
||||
for (const { path, content } of normalizedContent) {
|
||||
await this.vfs.writeFile(path, content);
|
||||
writtenCount++;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
scenario?.failure({
|
||||
message: 'Failed to commit all files to Vfs',
|
||||
error: this.logger?.stringifyError(error),
|
||||
filesWritten: writtenCount,
|
||||
totalFiles: normalizedContent.length,
|
||||
failedFile: normalizedContent[writtenCount]?.path,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
scenario?.complete({
|
||||
message: 'Successfully committed files to Vfs',
|
||||
filesWritten: writtenCount,
|
||||
totalFiles: normalizedContent.length,
|
||||
});
|
||||
}
|
||||
join(...paths) {
|
||||
return this.vfs.join(...paths);
|
||||
}
|
||||
async fileExists(path) {
|
||||
return !!this.tsMorphProject.getSourceFile(path);
|
||||
}
|
||||
async directoryExists(path) {
|
||||
return !!this.tsMorphProject.getDirectory(path);
|
||||
}
|
||||
async getSourceFilesInDirectory(path) {
|
||||
const directory = this.tsMorphProject.getDirectory(path);
|
||||
if (!directory) {
|
||||
return [];
|
||||
}
|
||||
return directory.getSourceFiles().map((file) => file.getBaseName());
|
||||
}
|
||||
getSourceFile(identifier) {
|
||||
const result = this.fileMap.get(identifier);
|
||||
if (!result) {
|
||||
throw new Error(`File with identifier ${identifier} not found. Possibly fetched before creation.`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async normalizeFile(sourceFile, scenario) {
|
||||
const rawContent = sourceFile.getFullText();
|
||||
const path = sourceFile.getFilePath();
|
||||
// 1. Insert copyright banner if not present
|
||||
if (!rawContent.includes(COPYRIGHT_BANNER.trim())) {
|
||||
sourceFile.insertText(0, COPYRIGHT_BANNER);
|
||||
}
|
||||
// 2. Fix missing imports and organize them
|
||||
try {
|
||||
sourceFile.fixMissingImports();
|
||||
sourceFile.organizeImports();
|
||||
}
|
||||
catch (error) {
|
||||
scenario?.failure({
|
||||
message: 'Failed to organize and fix missing imports',
|
||||
file: sourceFile.getBaseName(),
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
// 3. Run prettier
|
||||
let content;
|
||||
try {
|
||||
content = await prettier.format(sourceFile.getFullText(), {
|
||||
...defaultPrettierConfig,
|
||||
parser: 'typescript',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
scenario?.failure({
|
||||
message: 'Failed to format files with Prettier',
|
||||
file: sourceFile.getBaseName(),
|
||||
});
|
||||
const e = error;
|
||||
console.error('Prettier formatting error:', e.message, e.codeFrame ?? '');
|
||||
throw error;
|
||||
}
|
||||
return { content, path };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=project.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../../src/CodeGen/v2/project.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user