Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added better error handling and messaging for scripts.v2 #1042

Merged
merged 1 commit into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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