sp-devops
Version:
sp-devops is a command line utility to manage devops for Microsoft Sharepoint Online for modern Front-End projects
253 lines (236 loc) • 8.87 kB
JavaScript
const fetch = require('node-fetch');
const fs = require('fs');
const {
getFormDigestValue,
makePath,
logger,
getHeaders,
padGap,
throwError,
} = require('./utils');
const { getAccessToken } = require('./accesstoken');
const isListExists = async (siteUrl, subsite, listName, authorization) => {
let response = null;
try {
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')`, {
headers: getHeaders(authorization),
method: 'GET',
}).catch(throwError);
response = await response.json().catch(throwError);
response = !!(response.d);
} catch (e) {
throwError(e);
}
return response;
};
const getListColumns = async (siteUrl, subsite, listName, authorization) => {
let response = null;
const query = '$filter=Hidden eq false and ReadOnlyField eq false&$select=Title';
try {
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')/Fields?${query}`, {
headers: getHeaders(authorization),
method: 'GET',
}).catch(throwError);
response = await response.json().catch(throwError);
if (response.d && response.d.results) {
response = response.d.results.map((col) => col.Title);
} else response = [];
} catch (e) {
throwError(e);
}
return response;
};
const deleteItems = async (siteUrl, subsite, listName, itemIds, authorization, formDigest) => {
let count = 0;
const total = itemIds.length;
try {
for (const id of itemIds) {
logger.progress('Deleteing Item'.padEnd(padGap), (`${listName}(${++count}/${total})`).grey);
await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')/Items(${id})`, {
body: null,
headers: getHeaders(authorization, {
'X-HTTP-Method': 'DELETE',
'If-Match': '*',
'X-RequestDigest': formDigest,
}),
method: 'POST',
}).catch(throwError);
}
} catch (e) {
throwError(e);
}
return count;
// TODO : convert to batch
};
const getItemIds = async (siteUrl, subsite, listName, authorization, next) => {
const size = 1000;
let ids = [];
const url = `${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')/Items?$top=${size}&$select=Id`;
try {
let response = await fetch(next || url, {
headers: getHeaders(authorization),
method: 'GET',
}).catch(throwError);
response = await response.json().catch(throwError);
ids = (response.d.results || []).map((item) => item.Id);
if (response['odata.nextLink']) {
return ids.concat(
await getItemIds(siteUrl, subsite, listName, authorization, response['odata.nextLink']).catch(throwError),
);
}
} catch (e) {
throwError(e);
}
return ids;
};
const deleteList = async (siteUrl, subsite, listName, authorization, formDigest) => {
logger.info('Deleting List'.padEnd(padGap), listName.red);
let response = null;
try {
const itemIds = await getItemIds(siteUrl, subsite, listName, authorization).catch(throwError);
await deleteItems(siteUrl, subsite, listName, itemIds, authorization, formDigest).catch(throwError);
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')`, {
body: null,
headers: getHeaders(authorization, {
'X-HTTP-Method': 'DELETE',
'If-Match': '*',
'X-RequestDigest': formDigest,
}),
method: 'POST',
}).catch(throwError);
} catch (e) {
throwError(e);
}
return response;
};
const createList = async (siteUrl, subsite, listConfig, authorization, formDigest) => {
logger.info('Creating List'.padEnd(padGap), listConfig.name.green);
let response = null;
try {
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists`, {
body: JSON.stringify({
__metadata: { type: 'SP.List' },
BaseTemplate: 100,
Title: listConfig.name,
}),
headers: getHeaders(authorization, { 'X-RequestDigest': formDigest }),
method: 'POST',
}).catch(throwError);
response = await response.json().catch(throwError);
if (listConfig.addToQuickLaunch) {
await fetch(`${makePath(siteUrl, subsite)}/_api/web/navigation/QuickLaunch`, {
body: JSON.stringify({
__metadata: { type: 'SP.NavigationNode' },
Title: listConfig.name,
Url: `${makePath(siteUrl, subsite)}/Lists/${listConfig.name}/AllItems.aspx`,
}),
headers: getHeaders(authorization, { 'X-RequestDigest': formDigest }),
method: 'POST',
}).catch(throwError);
}
} catch (e) {
throwError(e);
}
return response;
};
const createColumn = async (siteUrl, subsite, listConfig, column, authorization, formDigest) => {
logger.info('Creating Column'.padEnd(padGap), (`${listConfig.name}.${column.Title}`).green);
let response = null;
try {
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listConfig.name}')/fields`, {
body: JSON.stringify(column),
headers: getHeaders(authorization, { 'X-RequestDigest': formDigest }),
method: 'POST',
}).catch(throwError);
response = await response.json().catch(throwError);
if (listConfig.addToView) {
const viewUrl = `Views/GetByTitle('All%20Items')/ViewFields/addViewField('${column.Title}')`;
await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listConfig.name}')/${viewUrl}`, {
body: null,
headers: getHeaders(authorization, { 'X-RequestDigest': formDigest }),
method: 'POST',
}).catch(throwError);
}
} catch (e) {
throwError(e);
}
return response;
};
const createColumns = async (siteUrl, subsite, listConfig, existingCols, authorization, formDigest) => {
const responses = [];
let response = null;
try {
for (const column of listConfig.columns) {
if (!existingCols.includes(column.Title)) {
response = await createColumn(siteUrl, subsite, listConfig, column, authorization, formDigest).catch(throwError);
responses.push(response);
}
}
} catch (e) {
throwError(e);
}
return responses;
};
const addItems = async (siteUrl, subsite, listName, data = [], authorization, formDigest) => {
let response = null;
const responses = [];
let count = 0;
try {
for (const item of data) {
logger.progress('Populating Data'.padEnd(padGap), `${listName}[${item.Title || (`Item${++count}`)}]`);
response = await fetch(`${makePath(siteUrl, subsite)}/_api/web/lists/getbytitle('${listName}')/Items`, {
body: JSON.stringify({ ...item, __metadata: { type: `SP.Data.${listName}ListItem` } }),
headers: getHeaders(authorization, { 'X-RequestDigest': formDigest }),
method: 'POST',
}).catch(throwError);
response = await response.json().catch(throwError);
responses.push(response);
}
} catch (e) {
throwError(e);
}
return responses;
};
const createLists = async (siteUrl, subsite, specFileCreateList, authorization, formDigest) => {
try {
const rawData = fs.readFileSync(specFileCreateList);
const specJson = JSON.parse(rawData);
for (const listConfig of specJson.config) {
const listExists = await isListExists(siteUrl, subsite, listConfig.name, authorization).catch(throwError);
let existingColumns = [];
if (listExists) {
if (listConfig.dropIfExists) {
await deleteList(siteUrl, subsite, listConfig.name, authorization, formDigest).catch(throwError);
await createList(siteUrl, subsite, listConfig, authorization, formDigest).catch(throwError);
} else {
existingColumns = await getListColumns(siteUrl, subsite, listConfig.name, authorization).catch(throwError);
}
} else {
await createList(siteUrl, subsite, listConfig, authorization, formDigest).catch(throwError);
}
await createColumns(siteUrl, subsite, listConfig, existingColumns, authorization, formDigest).catch(throwError);
await addItems(siteUrl, subsite, listConfig.name, listConfig.items, authorization, formDigest).catch(throwError);
}
} catch (e) {
throwError(e);
}
};
const setup = async ({
appClientId,
appClientSecret,
siteUrl,
subsite,
specFileCreateList,
authToken,
}) => {
logger.start('Operation Started'.bold, '😈');
try {
const authorization = authToken || await getAccessToken(siteUrl, subsite, appClientId, appClientSecret).catch(throwError);
const formDigest = await getFormDigestValue(siteUrl, subsite, authorization).catch(throwError);
await createLists(siteUrl, subsite, specFileCreateList, authorization, formDigest).catch(throwError);
logger.success('Operation Completed'.bold, '😈');
} catch (e) {
throwError(e);
}
};
exports.setup = setup;