@microsoft/m365-spec-parser 0.2.2-alpha.a0aeb8641.0 → 0.2.2-alpha.ab66695dd.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm2017.js +16 -21
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +56 -28
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +16 -21
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +56 -28
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +3 -1
- package/dist/src/interfaces.d.ts +6 -0
- package/dist/src/manifestUpdater.d.ts +3 -3
- package/dist/src/specParser.d.ts +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/package.json +3 -3
package/dist/index.esm2017.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import converter from 'swagger2openapi';
|
|
|
3
3
|
import jsyaml from 'js-yaml';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
5
|
import path from 'path';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
6
7
|
|
|
7
8
|
// Copyright (c) Microsoft Corporation.
|
|
8
9
|
/**
|
|
@@ -58,6 +59,7 @@ var WarningType;
|
|
|
58
59
|
WarningType["GenerateCardFailed"] = "generate-card-failed";
|
|
59
60
|
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
60
61
|
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
62
|
+
WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
|
|
61
63
|
WarningType["Unknown"] = "unknown";
|
|
62
64
|
})(WarningType || (WarningType = {}));
|
|
63
65
|
/**
|
|
@@ -96,6 +98,7 @@ ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please conve
|
|
|
96
98
|
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
97
99
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
98
100
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
101
|
+
ConstantString.FuncDescriptionTooLong = "The description of the function '%s' is too long. The current length is %s characters, while the maximum allowed length is %s characters.";
|
|
99
102
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
100
103
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
101
104
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -175,8 +178,9 @@ ConstantString.ConversationStarterMaxLens = 50;
|
|
|
175
178
|
ConstantString.CommandTitleMaxLens = 32;
|
|
176
179
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
177
180
|
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
181
|
+
ConstantString.FunctionDescriptionMaxLens = 100;
|
|
178
182
|
ConstantString.DefaultPluginId = "plugin_1";
|
|
179
|
-
ConstantString.PluginManifestSchema = "https://
|
|
183
|
+
ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.1/schema.json";
|
|
180
184
|
|
|
181
185
|
// Copyright (c) Microsoft Corporation.
|
|
182
186
|
class SpecParserError extends Error {
|
|
@@ -189,16 +193,19 @@ class SpecParserError extends Error {
|
|
|
189
193
|
// Copyright (c) Microsoft Corporation.
|
|
190
194
|
class Utils {
|
|
191
195
|
static hasNestedObjectInSchema(schema) {
|
|
192
|
-
if (schema
|
|
196
|
+
if (this.isObjectSchema(schema)) {
|
|
193
197
|
for (const property in schema.properties) {
|
|
194
198
|
const nestedSchema = schema.properties[property];
|
|
195
|
-
if (nestedSchema
|
|
199
|
+
if (this.isObjectSchema(nestedSchema)) {
|
|
196
200
|
return true;
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
}
|
|
200
204
|
return false;
|
|
201
205
|
}
|
|
206
|
+
static isObjectSchema(schema) {
|
|
207
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
208
|
+
}
|
|
202
209
|
static containMultipleMediaTypes(bodyObject) {
|
|
203
210
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
204
211
|
}
|
|
@@ -427,7 +434,7 @@ class Utils {
|
|
|
427
434
|
optionalParams.push(parameter);
|
|
428
435
|
}
|
|
429
436
|
}
|
|
430
|
-
else if (schema
|
|
437
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
431
438
|
const { properties } = schema;
|
|
432
439
|
for (const property in properties) {
|
|
433
440
|
let isRequired = false;
|
|
@@ -765,7 +772,7 @@ class Validator {
|
|
|
765
772
|
}
|
|
766
773
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
767
774
|
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
768
|
-
if (isCopilot &&
|
|
775
|
+
if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
|
|
769
776
|
paramResult.isValid = false;
|
|
770
777
|
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
771
778
|
return paramResult;
|
|
@@ -781,7 +788,7 @@ class Validator {
|
|
|
781
788
|
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
782
789
|
}
|
|
783
790
|
}
|
|
784
|
-
else if (schema
|
|
791
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
785
792
|
const { properties } = schema;
|
|
786
793
|
for (const property in properties) {
|
|
787
794
|
let isRequired = false;
|
|
@@ -817,7 +824,7 @@ class Validator {
|
|
|
817
824
|
for (let i = 0; i < paramObject.length; i++) {
|
|
818
825
|
const param = paramObject[i];
|
|
819
826
|
const schema = param.schema;
|
|
820
|
-
if (isCopilot &&
|
|
827
|
+
if (isCopilot && Utils.hasNestedObjectInSchema(schema)) {
|
|
821
828
|
paramResult.isValid = false;
|
|
822
829
|
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
823
830
|
continue;
|
|
@@ -860,17 +867,6 @@ class Validator {
|
|
|
860
867
|
}
|
|
861
868
|
return paramResult;
|
|
862
869
|
}
|
|
863
|
-
hasNestedObjectInSchema(schema) {
|
|
864
|
-
if (schema.type === "object") {
|
|
865
|
-
for (const property in schema.properties) {
|
|
866
|
-
const nestedSchema = schema.properties[property];
|
|
867
|
-
if (nestedSchema.type === "object") {
|
|
868
|
-
return true;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return false;
|
|
873
|
-
}
|
|
874
870
|
}
|
|
875
871
|
|
|
876
872
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -929,7 +925,7 @@ class CopilotValidator extends Validator {
|
|
|
929
925
|
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
930
926
|
if (requestJsonBody) {
|
|
931
927
|
const requestBodySchema = requestJsonBody.schema;
|
|
932
|
-
if (requestBodySchema
|
|
928
|
+
if (!Utils.isObjectSchema(requestBodySchema)) {
|
|
933
929
|
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
934
930
|
}
|
|
935
931
|
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
@@ -1376,7 +1372,7 @@ class AdaptiveCardGenerator {
|
|
|
1376
1372
|
return [template];
|
|
1377
1373
|
}
|
|
1378
1374
|
// some schema may not contain type but contain properties
|
|
1379
|
-
if (
|
|
1375
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1380
1376
|
const { properties } = schema;
|
|
1381
1377
|
const result = [];
|
|
1382
1378
|
for (const property in properties) {
|
|
@@ -1444,7 +1440,7 @@ class AdaptiveCardGenerator {
|
|
|
1444
1440
|
}
|
|
1445
1441
|
// Find the first array property in the response schema object with the well-known name
|
|
1446
1442
|
static getResponseJsonPathFromSchema(schema) {
|
|
1447
|
-
if (
|
|
1443
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1448
1444
|
const { properties } = schema;
|
|
1449
1445
|
for (const property in properties) {
|
|
1450
1446
|
const schema = properties[property];
|
|
@@ -1596,7 +1592,7 @@ function inferProperties(card) {
|
|
|
1596
1592
|
|
|
1597
1593
|
// Copyright (c) Microsoft Corporation.
|
|
1598
1594
|
class ManifestUpdater {
|
|
1599
|
-
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
|
|
1595
|
+
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo, existingPluginManifestInfo) {
|
|
1600
1596
|
const manifest = await fs.readJSON(manifestPath);
|
|
1601
1597
|
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1602
1598
|
manifest.copilotExtensions = manifest.copilotExtensions || {};
|
|
@@ -1612,7 +1608,7 @@ class ManifestUpdater {
|
|
|
1612
1608
|
}
|
|
1613
1609
|
const appName = this.removeEnvs(manifest.name.short);
|
|
1614
1610
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1615
|
-
const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
|
|
1611
|
+
const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo);
|
|
1616
1612
|
return [manifest, apiPlugin, warnings];
|
|
1617
1613
|
}
|
|
1618
1614
|
static updateManifestDescription(manifest, spec) {
|
|
@@ -1634,7 +1630,7 @@ class ManifestUpdater {
|
|
|
1634
1630
|
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
|
|
1635
1631
|
}
|
|
1636
1632
|
}
|
|
1637
|
-
static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
|
|
1633
|
+
static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options, existingPluginManifestInfo) {
|
|
1638
1634
|
var _a, _b, _c, _d;
|
|
1639
1635
|
const warnings = [];
|
|
1640
1636
|
const functions = [];
|
|
@@ -1682,7 +1678,7 @@ class ManifestUpdater {
|
|
|
1682
1678
|
if (requestBody) {
|
|
1683
1679
|
const requestJsonBody = requestBody.content["application/json"];
|
|
1684
1680
|
const requestBodySchema = requestJsonBody.schema;
|
|
1685
|
-
if (requestBodySchema
|
|
1681
|
+
if (Utils.isObjectSchema(requestBodySchema)) {
|
|
1686
1682
|
for (const property in requestBodySchema.properties) {
|
|
1687
1683
|
const schema = requestBodySchema.properties[property];
|
|
1688
1684
|
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
@@ -1693,9 +1689,18 @@ class ManifestUpdater {
|
|
|
1693
1689
|
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
1694
1690
|
}
|
|
1695
1691
|
}
|
|
1692
|
+
let funcDescription = operationItem.description || operationItem.summary || "";
|
|
1693
|
+
if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
|
|
1694
|
+
warnings.push({
|
|
1695
|
+
type: WarningType.FuncDescriptionTooLong,
|
|
1696
|
+
content: Utils.format(ConstantString.FuncDescriptionTooLong, safeFunctionName, funcDescription.length.toString(), ConstantString.FunctionDescriptionMaxLens.toString()),
|
|
1697
|
+
data: safeFunctionName,
|
|
1698
|
+
});
|
|
1699
|
+
funcDescription = funcDescription.slice(0, ConstantString.FunctionDescriptionMaxLens);
|
|
1700
|
+
}
|
|
1696
1701
|
const funcObj = {
|
|
1697
1702
|
name: safeFunctionName,
|
|
1698
|
-
description:
|
|
1703
|
+
description: funcDescription,
|
|
1699
1704
|
};
|
|
1700
1705
|
if (options.allowResponseSemantics) {
|
|
1701
1706
|
try {
|
|
@@ -1744,6 +1749,10 @@ class ManifestUpdater {
|
|
|
1744
1749
|
if (await fs.pathExists(apiPluginFilePath)) {
|
|
1745
1750
|
apiPlugin = await fs.readJSON(apiPluginFilePath);
|
|
1746
1751
|
}
|
|
1752
|
+
else if (existingPluginManifestInfo &&
|
|
1753
|
+
(await fs.pathExists(existingPluginManifestInfo.manifestPath))) {
|
|
1754
|
+
apiPlugin = await fs.readJSON(existingPluginManifestInfo.manifestPath);
|
|
1755
|
+
}
|
|
1747
1756
|
else {
|
|
1748
1757
|
apiPlugin = {
|
|
1749
1758
|
$schema: ConstantString.PluginManifestSchema,
|
|
@@ -1766,6 +1775,11 @@ class ManifestUpdater {
|
|
|
1766
1775
|
}
|
|
1767
1776
|
}
|
|
1768
1777
|
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1778
|
+
// Need to delete previous runtime since spec path has changed
|
|
1779
|
+
if (existingPluginManifestInfo) {
|
|
1780
|
+
const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
|
|
1781
|
+
apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
|
|
1782
|
+
}
|
|
1769
1783
|
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1770
1784
|
var _a, _b;
|
|
1771
1785
|
return runtime.spec.url === specRelativePath &&
|
|
@@ -1962,6 +1976,7 @@ class SpecParser {
|
|
|
1962
1976
|
*/
|
|
1963
1977
|
async validate() {
|
|
1964
1978
|
try {
|
|
1979
|
+
let hash = "";
|
|
1965
1980
|
try {
|
|
1966
1981
|
await this.loadSpec();
|
|
1967
1982
|
if (!this.parser.$refs.circular) {
|
|
@@ -1977,8 +1992,13 @@ class SpecParser {
|
|
|
1977
1992
|
status: ValidationStatus.Error,
|
|
1978
1993
|
warnings: [],
|
|
1979
1994
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
1995
|
+
specHash: hash,
|
|
1980
1996
|
};
|
|
1981
1997
|
}
|
|
1998
|
+
if (this.unResolveSpec.servers) {
|
|
1999
|
+
const serverString = JSON.stringify(this.unResolveSpec.servers);
|
|
2000
|
+
hash = createHash("sha256").update(serverString).digest("hex");
|
|
2001
|
+
}
|
|
1982
2002
|
const errors = [];
|
|
1983
2003
|
const warnings = [];
|
|
1984
2004
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
@@ -1988,6 +2008,7 @@ class SpecParser {
|
|
|
1988
2008
|
errors: [
|
|
1989
2009
|
{ type: ErrorType.SwaggerNotSupported, content: ConstantString.SwaggerNotSupported },
|
|
1990
2010
|
],
|
|
2011
|
+
specHash: hash,
|
|
1991
2012
|
};
|
|
1992
2013
|
}
|
|
1993
2014
|
// Remote reference not supported
|
|
@@ -2021,6 +2042,7 @@ class SpecParser {
|
|
|
2021
2042
|
status: status,
|
|
2022
2043
|
warnings: warnings,
|
|
2023
2044
|
errors: errors,
|
|
2045
|
+
specHash: hash,
|
|
2024
2046
|
};
|
|
2025
2047
|
}
|
|
2026
2048
|
catch (err) {
|
|
@@ -2136,7 +2158,7 @@ class SpecParser {
|
|
|
2136
2158
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
2137
2159
|
* @param pluginFilePath File path of the api plugin file to generate.
|
|
2138
2160
|
*/
|
|
2139
|
-
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
2161
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, existingPluginFilePath, signal) {
|
|
2140
2162
|
const result = {
|
|
2141
2163
|
allSuccess: true,
|
|
2142
2164
|
warnings: [],
|
|
@@ -2150,7 +2172,13 @@ class SpecParser {
|
|
|
2150
2172
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
2151
2173
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
2152
2174
|
}
|
|
2153
|
-
const
|
|
2175
|
+
const existingPluginManifestInfo = existingPluginFilePath
|
|
2176
|
+
? {
|
|
2177
|
+
manifestPath: existingPluginFilePath,
|
|
2178
|
+
specPath: this.pathOrSpec,
|
|
2179
|
+
}
|
|
2180
|
+
: undefined;
|
|
2181
|
+
const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo, existingPluginManifestInfo);
|
|
2154
2182
|
result.warnings.push(...warnings);
|
|
2155
2183
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
|
|
2156
2184
|
await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
|