Skip to content

Commit ce33653

Browse files
Onboard all resource types to autorest v2
1 parent 54a48c6 commit ce33653

File tree

321 files changed

+295
-105041
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

321 files changed

+295
-105041
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Setup .NET Core
2121
uses: actions/setup-dotnet@v4
2222
with:
23-
dotnet-version: '5.0.x'
23+
dotnet-version: '8.0.x'
2424

2525
- name: Install NPM modules
2626
run: npm ci

bicep-types-az

generator/autogenlist.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const disabledProviders: AutoGenConfig[] = [
3434
{
3535
basePath: 'cdn/resource-manager',
3636
namespace: 'Microsoft.Cdn',
37-
useAutorestV2: true,
3837
},
3938
{
4039
basePath: 'logic/resource-manager',
@@ -563,7 +562,6 @@ const autoGenList: AutoGenConfig[] = [
563562
{
564563
basePath: 'mysql/resource-manager',
565564
namespace: 'Microsoft.DBforMySQL',
566-
useAutorestV2: true,
567565
},
568566
{
569567
basePath: 'managementgroups/resource-manager',
@@ -618,7 +616,6 @@ const autoGenList: AutoGenConfig[] = [
618616
{
619617
basePath: 'postgresql/resource-manager',
620618
namespace: 'Microsoft.DBforPostgreSQL',
621-
useAutorestV2: true,
622619
},
623620
{
624621
basePath: 'postgresqlhsc/resource-manager',
@@ -707,18 +704,15 @@ const autoGenList: AutoGenConfig[] = [
707704
basePath: 'recoveryservicessiterecovery/resource-manager',
708705
namespace: 'Microsoft.RecoveryServices',
709706
suffix: 'SiteRecovery',
710-
useAutorestV2: true,
711707
},
712708
{
713709
basePath: 'recoveryservicesbackup/resource-manager',
714710
namespace: 'Microsoft.RecoveryServices',
715711
suffix: "Backup",
716-
useAutorestV2: true,
717712
},
718713
{
719714
basePath: "recoveryservices/resource-manager",
720715
namespace: "Microsoft.RecoveryServices",
721-
useAutorestV2: true,
722716
},
723717
{
724718
basePath: 'reservations/resource-manager',
@@ -864,7 +858,6 @@ const autoGenList: AutoGenConfig[] = [
864858
basePath: 'storage/resource-manager',
865859
namespace: 'Microsoft.Storage',
866860
postProcessor: storageProcessor,
867-
useAutorestV2: true,
868861
},
869862
{
870863
basePath: 'compute/resource-manager',
@@ -882,7 +875,6 @@ const autoGenList: AutoGenConfig[] = [
882875
{
883876
basePath: 'sql/resource-manager',
884877
namespace: 'Microsoft.Sql',
885-
useAutorestV2: true,
886878
},
887879
{
888880
basePath: 'scheduler/resource-manager',
@@ -1051,17 +1043,14 @@ const autoGenList: AutoGenConfig[] = [
10511043
{
10521044
basePath: 'web/resource-manager',
10531045
namespace: 'Microsoft.CertificateRegistration',
1054-
useAutorestV2: true,
10551046
},
10561047
{
10571048
basePath: 'web/resource-manager',
10581049
namespace: 'Microsoft.DomainRegistration',
1059-
useAutorestV2: true,
10601050
},
10611051
{
10621052
basePath: 'web/resource-manager',
10631053
namespace: 'Microsoft.Web',
1064-
useAutorestV2: true,
10651054
},
10661055
{
10671056
basePath: 'deviceupdate/resource-manager',
@@ -1076,45 +1065,38 @@ const autoGenList: AutoGenConfig[] = [
10761065
basePath: 'trafficmanager/resource-manager',
10771066
namespace: 'Microsoft.Network',
10781067
suffix: 'TrafficManager',
1079-
useAutorestV2: true,
10801068
},
10811069
{
10821070
basePath: 'network/resource-manager',
10831071
namespace: 'Microsoft.Network',
10841072
postProcessor: networkPostProcessor,
10851073
suffix: 'NRP',
1086-
useAutorestV2: true,
10871074
},
10881075
{
10891076
basePath: 'dnsresolver/resource-manager',
10901077
namespace: 'Microsoft.Network',
10911078
suffix: 'DnsResolver',
1092-
useAutorestV2: true,
10931079
},
10941080
{
10951081
basePath: 'frontdoor/resource-manager',
10961082
namespace: 'Microsoft.Network',
10971083
suffix: 'FrontDoor',
1098-
useAutorestV2: true,
10991084
},
11001085
{
11011086
basePath: 'dns/resource-manager',
11021087
namespace: 'Microsoft.Network',
11031088
suffix: 'DNS',
1104-
useAutorestV2: true,
11051089
},
11061090
{
11071091
basePath: 'privatedns/resource-manager',
11081092
namespace: 'Microsoft.Network',
11091093
suffix: 'privateDns',
1110-
useAutorestV2: true,
11111094
},
11121095
{
11131096
//Pause autogeneration until errors are fixed
11141097
basePath: 'azurestackhci/resource-manager',
11151098
namespace: 'Microsoft.AzureStackHCI',
11161099
postProcessor: azureStackHciPostProcessor,
1117-
useAutorestV2: true,
11181100
},
11191101
{
11201102
basePath: 'advisor/resource-manager',
@@ -1142,38 +1124,31 @@ const autoGenList: AutoGenConfig[] = [
11421124
basePath: 'hdinsight/resource-manager/Microsoft.HDInsight/HDInsightOnAks',
11431125
namespace: 'Microsoft.HDInsight',
11441126
useNamespaceFromConfig: true,
1145-
useAutorestV2: true,
11461127
suffix: 'OnAks',
11471128
},
11481129
{
11491130
basePath: 'developerhub/resource-manager',
11501131
namespace: 'Microsoft.DevHub',
1151-
useAutorestV2: true,
11521132
},
11531133
{
11541134
basePath: 'domainservices/resource-manager',
11551135
namespace: 'Microsoft.AAD',
1156-
useAutorestV2: true,
11571136
},
11581137
{
11591138
basePath: 'edgeorderpartner/resource-manager',
11601139
namespace: 'Microsoft.EdgeOrderPartner',
1161-
useAutorestV2: true,
11621140
},
11631141
{
11641142
basePath: 'servicefabricmesh/resource-manager',
11651143
namespace: 'Microsoft.ServiceFabricMesh',
1166-
useAutorestV2: true,
11671144
},
11681145
{
11691146
basePath: 'operationalinsights/resource-manager',
11701147
namespace: 'Microsoft.OperationalInsights',
1171-
useAutorestV2: true,
11721148
},
11731149
{
11741150
basePath: 'confidentialLedger/resource-manager',
11751151
namespace: 'Microsoft.ConfidentialLedger',
1176-
useAutorestV2: true
11771152
},
11781153
];
11791154

generator/autorest.ts

Lines changed: 96 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,97 @@
22
// Licensed under the MIT License.
33
import path from 'path';
44
import os from 'os';
5-
import { findRecursive, lowerCaseContains, executeCmd, fileExists } from './utils';
5+
import { findRecursive, executeCmd, fileExists } from './utils';
66
import * as constants from './constants';
7-
import { ReadmeTag, AutoGenConfig, CodeBlock } from './models';
8-
import * as cm from '@ts-common/commonmark-to-markdown'
9-
import * as yaml from 'js-yaml'
107
import { readFile, writeFile } from 'fs/promises';
8+
import * as markdown from '@ts-common/commonmark-to-markdown'
9+
import * as yaml from 'js-yaml'
1110

1211
const autorestBinary = os.platform() === 'win32' ? 'autorest.cmd' : 'autorest';
13-
export const apiVersionRegex = /^\d{4}-\d{2}-\d{2}(|-preview)$/;
12+
13+
const rootDir = `${__dirname}/../`;
14+
const extensionDir = path.resolve(`${rootDir}/bicep-types-az/src/autorest.bicep/`);
15+
16+
export async function generateAutorestConfig(readmePath: string, bicepReadmePath: string) {
17+
// We expect a path format convention of <provider>/(any/number/of/intervening/folders)/<yyyy>-<mm>-<dd>(|-preview)/<filename>.json
18+
// This information is used to generate individual tags in the generated autorest configuration
19+
// eslint-disable-next-line no-useless-escape
20+
const pathRegex = /^(\$\(this-folder\)\/|)([^\/]+)(?:\/[^\/]+)*\/(\d{4}-\d{2}-\d{2}(|-preview))\/.*\.json$/i;
21+
22+
const readmeContents = await readFile(readmePath, { encoding: 'utf8' });
23+
const readmeMarkdown = markdown.parse(readmeContents);
24+
25+
const inputFiles = new Set<string>();
26+
// we need to look for all autorest configuration elements containing input files, and collect that list of files. These will look like (e.g.):
27+
// ```yaml $(tag) == 'someTag'
28+
// input-file:
29+
// - path/to/file.json
30+
// - path/to/other_file.json
31+
// ```
32+
for (const node of markdown.iterate(readmeMarkdown.markDown)) {
33+
// We're only interested in yaml code blocks
34+
if (node.type !== 'code_block' || !node.info || !node.literal ||
35+
!node.info.trim().startsWith('yaml')) {
36+
continue;
37+
}
38+
39+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
40+
const yamlData = yaml.load(node.literal) as any;
41+
if (yamlData) {
42+
// input-file may be a single string or an array of strings
43+
const inputFile = yamlData['input-file'];
44+
if (typeof inputFile === 'string') {
45+
inputFiles.add(inputFile);
46+
} else if (inputFile instanceof Array) {
47+
for (const i of inputFile) {
48+
inputFiles.add(i);
49+
}
50+
}
51+
}
52+
}
53+
54+
const filesByTag: Record<string, string[]> = {};
55+
for (const file of inputFiles) {
56+
const normalizedFile = normalizeJsonPath(file);
57+
const match = pathRegex.exec(normalizedFile);
58+
if (match) {
59+
// Generate a unique tag. We can't process all of the different API versions in one autorest pass
60+
// because there are constraints on naming uniqueness (e.g. naming of definitions), so we want to pass over
61+
// each API version separately.
62+
const tagName = `${match[2].toLowerCase()}-${match[3].toLowerCase()}`;
63+
if (!filesByTag[tagName]) {
64+
filesByTag[tagName] = [];
65+
}
66+
67+
filesByTag[tagName].push(normalizedFile);
68+
} else {
69+
console.warn(`WARNING: Unable to parse swagger path "${file}"`);
70+
}
71+
}
72+
73+
let generatedContent = `##Bicep
74+
75+
### Bicep multi-api
76+
\`\`\`yaml $(bicep) && $(multiapi)
77+
${yaml.dump({ 'batch': Object.keys(filesByTag).map(tag => ({ 'tag': tag })) }, { lineWidth: 1000 })}
78+
\`\`\`
79+
`;
80+
81+
for (const tag of Object.keys(filesByTag)) {
82+
generatedContent += `### Tag: ${tag} and bicep
83+
\`\`\`yaml $(tag) == '${tag}' && $(bicep)
84+
${yaml.dump({ 'input-file': filesByTag[tag] }, { lineWidth: 1000})}
85+
\`\`\`
86+
`;
87+
}
88+
89+
await writeFile(bicepReadmePath, generatedContent);
90+
}
91+
92+
function normalizeJsonPath(jsonPath: string) {
93+
// eslint-disable-next-line no-useless-escape
94+
return path.normalize(jsonPath).replace(/[\\\/]/g, '/');
95+
}
1496

1597
async function execAutoRest(tmpFolder: string, params: string[]) {
1698
await executeCmd(__dirname, `${__dirname}/node_modules/.bin/${autorestBinary}`, params);
@@ -23,13 +105,17 @@ async function execAutoRest(tmpFolder: string, params: string[]) {
23105

24106
export async function runAutorest(readme: string, tmpFolder: string) {
25107
const autoRestParams = [
26-
`--version=${constants.autorestCoreVersion}`,
27-
`--use=@autorest/azureresourceschema@${constants.azureresourceschemaVersion}`,
28-
'--azureresourceschema',
108+
`--use=@autorest/modelerfour`,
109+
`--use=${extensionDir}`,
110+
'--bicep',
29111
`--output-folder=${tmpFolder}`,
30112
'--multiapi',
31-
'--pass-thru:subset-reducer',
32-
'--pass-thru:schema-validator-swagger',
113+
'--title=none',
114+
// This is necessary to avoid failures such as "ERROR: Semantic violation: Discriminator must be a required property." blocking type generation.
115+
// In an ideal world, we'd raise issues in https://github.com/Azure/azure-rest-api-specs and force RP teams to fix them, but this isn't very practical
116+
// as new validations are added continuously, and there's often quite a lag before teams will fix them - we don't want to be blocked by this in generating types.
117+
`--skip-semantics-validation`,
118+
`--arm-schema=true`,
33119
readme,
34120
];
35121

@@ -38,83 +124,4 @@ export async function runAutorest(readme: string, tmpFolder: string) {
38124
}
39125

40126
return await execAutoRest(tmpFolder, autoRestParams);
41-
}
42-
43-
44-
45-
export async function generateAutorestConfig(readme: string, autoGenConfig: AutoGenConfig) {
46-
const content = (await readFile(readme)).toString();
47-
const markdownEx = cm.parse(content);
48-
const fileSet = new Set<string>();
49-
for (const node of cm.iterate(markdownEx.markDown)) {
50-
// We're only interested in yaml code blocks
51-
if (node.type !== 'code_block' || !node.info || !node.literal ||
52-
!node.info.trim().startsWith('yaml')) {
53-
continue;
54-
}
55-
56-
const DOC = (yaml.load(node.literal) as CodeBlock);
57-
if (DOC) {
58-
const inputFile = DOC['input-file'];
59-
if (typeof inputFile === 'string') {
60-
fileSet.add(inputFile);
61-
} else if (inputFile instanceof Array) {
62-
for (const i of inputFile) {
63-
fileSet.add(i);
64-
}
65-
}
66-
}
67-
}
68-
69-
let readmeTag = {} as ReadmeTag;
70-
for (const inputFile of fileSet) {
71-
const pathComponents = inputFile.split("/");
72-
73-
if (!autoGenConfig.useNamespaceFromConfig &&
74-
!lowerCaseContains(pathComponents, autoGenConfig.namespace)) {
75-
continue;
76-
}
77-
78-
const apiVersion = pathComponents.filter(p => p.match(apiVersionRegex) !== null)[0];
79-
if (!apiVersion) {
80-
continue;
81-
}
82-
83-
readmeTag[apiVersion] ??= readmeTag[apiVersion] || [];
84-
readmeTag[apiVersion].push(inputFile);
85-
}
86-
87-
if (autoGenConfig.readmeTag) {
88-
readmeTag = {...readmeTag, ...autoGenConfig.readmeTag };
89-
}
90-
91-
const schemaReadmeContent = compositeSchemaReadme(readmeTag);
92-
93-
const schemaReadme = readme.replace(/\.md$/i, '.azureresourceschema.md');
94-
95-
await writeFile(schemaReadme, schemaReadmeContent);
96-
}
97-
98-
function compositeSchemaReadme(readmeTag: ReadmeTag): string {
99-
let content =
100-
`## AzureResourceSchema
101-
102-
### AzureResourceSchema multi-api
103-
104-
\`\`\` yaml $(azureresourceschema) && $(multiapi)
105-
${yaml.dump({ 'batch': Object.keys(readmeTag).map(apiVersion => ({ 'tag': `schema-${apiVersion}`})) }, { lineWidth: 1000 })}
106-
\`\`\`
107-
108-
`
109-
for (const apiVersion of Object.keys(readmeTag)) {
110-
content +=
111-
`
112-
### Tag: schema-${apiVersion} and azureresourceschema
113-
114-
\`\`\` yaml $(tag) == 'schema-${apiVersion}' && $(azureresourceschema)
115-
${yaml.dump({ 'input-file': readmeTag[apiVersion] }, { lineWidth: 1000})}
116-
\`\`\`
117-
`
118-
}
119-
return content;
120127
}

0 commit comments

Comments
 (0)