Skip to content

Commit

Permalink
Added better error handling and messaging for scripts.v2 (#1042)
Browse files Browse the repository at this point in the history
  • Loading branch information
azaslonov committed Nov 18, 2020
1 parent 719b780 commit 507d3f5
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 125 deletions.
84 changes: 52 additions & 32 deletions scripts.v2/capture.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,82 @@ const destinationFolder = process.argv[4];


async function getContentTypes() {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes?api-version=2019-12-01`, managementApiAccessToken);
const contentTypes = data.value.map(x => x.id.replace("\/contentTypes\/", ""));
try {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes?api-version=2019-12-01`, managementApiAccessToken);
const contentTypes = data.value.map(x => x.id.replace("\/contentTypes\/", ""));

return contentTypes;
return contentTypes;
}
catch (error) {
throw new Error(`Unable to fetch content types. ${error.message}`);
}
}

async function getContentItems(contentType) {
const contentItems = [];
let nextPageUrl = `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes/${contentType}/contentItems?api-version=2019-12-01`;
try {
const contentItems = [];
let nextPageUrl = `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes/${contentType}/contentItems?api-version=2019-12-01`;

do {
const data = await request("GET", nextPageUrl, managementApiAccessToken);
contentItems.push(...data.value);
do {
const data = await request("GET", nextPageUrl, managementApiAccessToken);
contentItems.push(...data.value);

if (data.value.length > 0 && data.nextLink) {
nextPageUrl = data.nextLink;
}
else {
nextPageUrl = null;
if (data.value.length > 0 && data.nextLink) {
nextPageUrl = data.nextLink;
}
else {
nextPageUrl = null;
}
}
}
while (nextPageUrl)
while (nextPageUrl)

return contentItems;
return contentItems;
}
catch (error) {
throw new Error(`Unable to fetch content items. ${error.message}`);
}
}

async function captureJson() {
const result = {};
const contentTypes = await getContentTypes();
try {
const result = {};
const contentTypes = await getContentTypes();

for (const contentType of contentTypes) {
const contentItems = await getContentItems(contentType);
for (const contentType of contentTypes) {
const contentItems = await getContentItems(contentType);

contentItems.forEach(contentItem => {
result[contentItem.id] = contentItem;
delete contentItem.id;
});
}
contentItems.forEach(contentItem => {
result[contentItem.id] = contentItem;
delete contentItem.id;
});
}

await fs.promises.mkdir(path.resolve(destinationFolder), { recursive: true });
await fs.promises.mkdir(path.resolve(destinationFolder), { recursive: true });

fs.writeFileSync(`${destinationFolder}/data.json`, JSON.stringify(result));
fs.writeFileSync(`${destinationFolder}/data.json`, JSON.stringify(result));
}
catch (error) {
throw new Error(`Unable to capture content. ${error.message}`);
}
}

async function capture() {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);
const localMediaFolder = `./${destinationFolder}/media`;
try {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);
const localMediaFolder = `./${destinationFolder}/media`;

await captureJson();
await downloadBlobs(blobStorageUrl, localMediaFolder);
await captureJson();
await downloadBlobs(blobStorageUrl, localMediaFolder);
}
catch (error) {
throw new Error(`Unable to complete export. ${error.message}`);
}
}

capture()
.then(() => {
console.log("DONE");
})
.catch(error => {
console.log(error);
console.log(error.message);
});
50 changes: 35 additions & 15 deletions scripts.v2/cleanup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,62 @@ const managementApiAccessToken = process.argv[3];


async function getContentTypes() {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes?api-version=2019-12-01`, managementApiAccessToken);
const contentTypes = data.value.map(x => x.id.replace("\/contentTypes\/", ""));
try {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes?api-version=2019-12-01`, managementApiAccessToken);
const contentTypes = data.value.map(x => x.id.replace("\/contentTypes\/", ""));

return contentTypes;
return contentTypes;
}
catch (error) {
throw new Error(`Unable to fetch content types. ${error.message}`);
}
}

async function getContentItems(contentType) {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes/${contentType}/contentItems?api-version=2019-12-01`, managementApiAccessToken);
const contentItems = data.value;
try {
const data = await request("GET", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/contentTypes/${contentType}/contentItems?api-version=2019-12-01`, managementApiAccessToken);
const contentItems = data.value;

return contentItems;
return contentItems;
}
catch (error) {
throw new Error(`Unable to fetch content items. ${error.message}`);
}
}

async function deleteContent() {
const contentTypes = await getContentTypes();
try {
const contentTypes = await getContentTypes();

for (const contentType of contentTypes) {
const contentItems = await getContentItems(contentType);
for (const contentType of contentTypes) {
const contentItems = await getContentItems(contentType);

for (const contentItem of contentItems) {
await request("DELETE", `https://${managementApiEndpoint}//subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/${contentItem.id}?api-version=2019-12-01`, managementApiAccessToken);
for (const contentItem of contentItems) {
await request("DELETE", `https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/${contentItem.id}?api-version=2019-12-01`, managementApiAccessToken);
}
}
}
catch (error) {
throw new Error(`Unable to delete content. ${error.message}`);
}
}

async function cleanup() {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);
try {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);

await deleteContent();
await deleteBlobs(blobStorageUrl);
await deleteContent();
await deleteBlobs(blobStorageUrl);
}
catch (error) {
throw new Error(`Unable to complete cleanup. ${error.message}`);
}
}

cleanup()
.then(() => {
console.log("DONE");
})
.catch(error => {
console.log(error);
console.log(error.message);
});
38 changes: 24 additions & 14 deletions scripts.v2/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,41 @@ const sourceFolder = process.argv[4];


async function generateJson() {
const data = fs.readFileSync(`${sourceFolder}/data.json`);
const dataObj = JSON.parse(data);
const keys = Object.keys(dataObj);
try {
const data = fs.readFileSync(`${sourceFolder}/data.json`);
const dataObj = JSON.parse(data);
const keys = Object.keys(dataObj);

for (const key of keys) {
await request(
"PUT",
`https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/${key}?api-version=2019-12-01`,
managementApiAccessToken,
JSON.stringify(dataObj[key]));
for (const key of keys) {
await request(
"PUT",
`https://${managementApiEndpoint}/subscriptions/00000/resourceGroups/00000/providers/Microsoft.ApiManagement/service/00000/${key}?api-version=2019-12-01`,
managementApiAccessToken,
JSON.stringify(dataObj[key]));
}
}
catch (error) {
throw new Error(`Unable to generate the content. ${error.message}`);
}
}

async function generate() {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);
const localMediaFolder = `./${sourceFolder}/media`;
try {
const blobStorageUrl = await getStorageSasTokenOrThrow(managementApiEndpoint, managementApiAccessToken);
const localMediaFolder = `./${sourceFolder}/media`;

await generateJson();
await uploadBlobs(blobStorageUrl, localMediaFolder);
await generateJson();
await uploadBlobs(blobStorageUrl, localMediaFolder);
}
catch (error) {
throw new Error(`Unable to complete import. ${error.message}`);
}
}

generate()
.then(() => {
console.log("DONE");
})
.catch(error => {
console.log(error);
console.log(error.message);
});
8 changes: 8 additions & 0 deletions scripts.v2/migrate.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@REM This script automates content migration between developer portal instances.

node ./migrate ^
--sourceEndpoint "<name.management.azure-api.net>" ^
--sourceToken "<token>" ^
--destEndpoint "<name.management.azure-api.net>" ^
--destToken "<token>" ^
--publishEndpoint "<name.developer.azure-api.net>"
54 changes: 32 additions & 22 deletions scripts.v2/migrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,31 +100,36 @@ const yargs = require('yargs')
.argv;

async function run() {
const sourceManagementApiEndpoint = yargs.sourceEndpoint;
const sourceManagementApiAccessToken = await getTokenOrThrow(yargs.sourceToken, yargs.sourceId, yargs.sourceKey);
try {
const sourceManagementApiEndpoint = yargs.sourceEndpoint;
const sourceManagementApiAccessToken = await getTokenOrThrow(yargs.sourceToken, yargs.sourceId, yargs.sourceKey);

const destManagementApiEndpoint = yargs.destEndpoint;
const destManagementApiAccessToken = await getTokenOrThrow(yargs.destToken, yargs.destId, yargs.destKey);
const publishEndpoint = yargs.publishEndpoint;
const destManagementApiEndpoint = yargs.destEndpoint;
const destManagementApiAccessToken = await getTokenOrThrow(yargs.destToken, yargs.destId, yargs.destKey);
const publishEndpoint = yargs.publishEndpoint;

// the rest of this mirrors migrate.bat, but since we're JS, we're platform-agnostic.
const snapshotFolder = '../dist/snapshot';
// the rest of this mirrors migrate.bat, but since we're JS, we're platform-agnostic.
const snapshotFolder = '../dist/snapshot';

// capture the content of the source portal
execSync(`node ./capture ${sourceManagementApiEndpoint} "${sourceManagementApiAccessToken}" "${snapshotFolder}"`);
// capture the content of the source portal
execSync(`node ./capture ${sourceManagementApiEndpoint} "${sourceManagementApiAccessToken}" "${snapshotFolder}"`);

// remove all content of the target portal
execSync(`node ./cleanup ${destManagementApiEndpoint} "${destManagementApiAccessToken}"`);
// remove all content of the target portal
execSync(`node ./cleanup ${destManagementApiEndpoint} "${destManagementApiAccessToken}"`);

// upload the content of the source portal
execSync(`node ./generate ${destManagementApiEndpoint} "${destManagementApiAccessToken}" "${snapshotFolder}"`);
// upload the content of the source portal
execSync(`node ./generate ${destManagementApiEndpoint} "${destManagementApiAccessToken}" "${snapshotFolder}"`);

if (publishEndpoint && !yargs.selfHosted) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
await publish(publishEndpoint, destManagementApiAccessToken);
if (publishEndpoint && !yargs.selfHosted) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
await publish(publishEndpoint, destManagementApiAccessToken);
}
else if (publishEndpoint) {
console.warn("Auto-publishing self-hosted portal is not supported.");
}
}
else if (publishEndpoint) {
console.warn("Auto-publishing self-hosted portal is not supported.");
catch (error) {
throw new Error(`Unable to complete migration. ${error.message}`);
}
}

Expand Down Expand Up @@ -172,17 +177,22 @@ async function generateSASToken(id, key, expiresIn = 3600) {
* @param {string} token the SAS token
*/
async function publish(endpoint, token) {
const url = `https://${endpoint}/publish`;
try {
const url = `https://${endpoint}/publish`;

// returns with literal OK (missing quotes), which is invalid json.
await request("POST", url, token);
// returns with literal OK (missing quotes), which is invalid json.
await request("POST", url, token);
}
catch (error) {
throw new Error(`Unable to schedule website publishing. ${error.message}`);
}
}

run()
.then(() => {
console.log("DONE");
})
.catch(error => {
console.error(error);
console.error(error.message);
process.exitCode = 1;
});
Loading

0 comments on commit 507d3f5

Please sign in to comment.