Skip to content

Instantly share code, notes, and snippets.

@iOnline247
Last active March 10, 2021 21:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iOnline247/cc8d68cb611b056695434224e6c2aa19 to your computer and use it in GitHub Desktop.
Save iOnline247/cc8d68cb611b056695434224e6c2aa19 to your computer and use it in GitHub Desktop.
Wrapper for SharePoint REST API.
/*!
* Created by Matthew Bramer
* Released under the MIT license
* Date: 2016-07-11
* Props to: http://blogs.msmvps.com/windsor/2015/02/13/reduce-code-need-for-rest-api-calls-with-sprestrepository/
* Tested using SharePoint Online & 2013 On-Prem.
*/
// http://sharepoint.stackexchange.com/questions/74978/can-i-tell-what-version-of-sharepoint-is-being-used-from-javascript
(function () {
const PRIVATE = "DO_NOT_TOUCH_OR_YOU'LL_BE_FIRED";
// TODO:
// Get the functions to pass ESLint.
//#region Utils
function buildUrl(url, api) {
// TODO:
// Test this from an add-in.
url = concatAppUrl(url, api.appUrl);
if (api.hostUrl) {
var delimiter = '_api/';
var index = url.indexOf(delimiter);
url =
url.slice(0, index + delimiter.length) +
'SP.AppContextSite(@target)' +
url.slice(index + delimiter.length - 1);
var connector = '?';
if (url.indexOf('?') > -1 && url.indexOf('$') > -1) {
connector = '&';
}
url = url + connector + "@target='" + api.hostUrl + "'";
}
return url;
}
function removeTrailingSlash(v) {
if (v === '/') {
return v;
}
return v.replace(/\/$/, '');
}
function concatAppUrl(url, appUrl) {
if (url.indexOf(appUrl) === -1) {
url = url.charAt(0) === '/' ? url : '/' + url;
url = appUrl + url;
}
return url;
}
function padPayload(data, url, odataType) {
// Determines what type of request is being sent.
// If it's verbose, then the payload needs to be
// padded with __metadata.
const isNotString = typeof data !== 'string';
const shouldAddMetadata = odataType === 'verbose' ? true : false;
const isCamlQuery = data.query && data.query.ViewXml ? true : false;
const payload = Object.assign({}, data);
// Set to `data` in case the payload is a string.
let output = data;
if (isNotString) {
if (shouldAddMetadata) {
if (isCamlQuery) {
payload.query.__metadata = {
type: 'SP.CamlQuery',
};
} else {
const rfindListName = /\('(.+)'\)/i;
const listName = url.match(rfindListName)[1];
payload.__metadata = {
type: 'SP.Data.{0}ListItem'.replace('{0}', listName),
};
}
}
output = JSON.stringify(payload);
}
return output;
}
//#endregion
function getOdataHeader(odataType) {
return 'application/json;odata=' + this.get_odataType();
}
function getOdataType() {
// Using nometadata can result in errors: { status: "parsererror", message: "Invalid character" }
// Use 'verbose' instead if your version doesn't support.
return this[PRIVATE]._odataType;
}
function setOdataType(odataType) {
this[PRIVATE]._odataType = odataType;
}
function getFormDigest() {
return this[PRIVATE]._formDigest;
}
async function setFormDigest(formDigest) {
const that = this;
if (!formDigest) {
return this.get_contextInfo().then(function (results) {
that[PRIVATE]._formDigest = results;
that[PRIVATE]._getContext = null;
return results;
});
}
this[PRIVATE]._formDigest = formDigest;
}
async function getContextInfo() {
const contextResponse = await fetch(`${this.appUrl}/_api/contextinfo`, {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json; odata=verbose',
},
});
const data = await contextResponse.json();
return data.d.GetContextWebInformation.FormDigestValue;
}
async function get(options) {
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
var opt = Object.assign(
{},
{
odataHeader: this.get_odataHeader(),
},
options
);
const response = await fetch(buildUrl(opt.url, this), {
method: 'GET',
credentials: 'include',
headers: {
Accept: opt.odataHeader,
},
});
return response.json();
}
async function add(options) {
const that = this;
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
const opt = Object.assign(
{},
{
credentials: 'include',
body: {},
odataHeader: that.get_odataHeader(),
},
options
);
const ajaxOpts = {
method: 'POST',
body: padPayload(opt.data, opt.url, that.get_odataType()),
headers: {
Accept: opt.odataHeader,
'Content-Type': opt.odataHeader,
'X-RequestDigest': that.get_formDigest(),
},
};
if (that[PRIVATE]._getContext) {
return that[PRIVATE]._getContext.then(
async function getCtxThenable() {
ajaxOpts.headers['X-RequestDigest'] = that.get_formDigest();
return (
await fetch(buildUrl(opt.url, that), ajaxOpts)
).json();
}
);
}
return (await fetch(buildUrl(opt.url, that), ajaxOpts)).json();
}
async function update(options) {
const that = this;
const opt = Object.assign(
{},
{
credentials: 'include',
body: {},
etag: '*',
httpMethod: 'PATCH',
odataHeader: this.get_odataHeader(),
},
options
);
const ajaxOpts = {
method: 'POST',
body: padPayload(opt.data, opt.url, that.get_odataType()),
headers: {
Accept: opt.odataHeader,
'Content-Type': opt.odataHeader,
'IF-MATCH': opt.etag,
'X-Http-Method': opt.httpMethod,
'X-RequestDigest': that.get_formDigest(),
},
};
if (that[PRIVATE]._getContext) {
return that[PRIVATE]._getContext.then(function getCtxThenable() {
ajaxOpts.headers['X-RequestDigest'] = that.get_formDigest();
return fetch(buildUrl(opt.url, that), ajaxOpts);
});
}
return fetch(buildUrl(opt.url, that), ajaxOpts);
}
async function deleteItem(options) {
const that = this;
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
const opt = Object.assign(
{},
{
etag: '*',
odataHeader: that.get_odataHeader(),
},
options
);
const ajaxOpts = {
method: 'DELETE',
headers: {
Accept: opt.odataHeader,
'IF-MATCH': opt.etag,
'X-RequestDigest': that.get_formDigest(),
},
};
if (that[PRIVATE]._getContext) {
return that[PRIVATE]._getContext.then(function getCtxThenable() {
ajaxOpts.headers['X-RequestDigest'] = that.get_formDigest();
return fetch(buildUrl(opt.url, that), ajaxOpts);
});
}
return fetch(buildUrl(opt.url, that), ajaxOpts);
}
async function camlQuery(options) {
const that = this;
const opt = Object.assign(
{},
{
viewXml: '<View></View>',
odataHeader: that.get_odataHeader(),
formDigest: options.formDigest,
},
options
);
opt.body = {
query: {
ViewXml: opt.viewXml,
},
};
const ajaxOpts = {
method: 'POST',
body: padPayload(opt.body, opt.url, that.get_odataType()),
headers: {
Accept: opt.odataHeader,
'Content-Type': opt.odataHeader,
'X-RequestDigest': opt.formDigest,
},
};
if (that[PRIVATE]._getContext) {
return that[PRIVATE]._getContext.then(
async function getCtxThenable() {
ajaxOpts.headers['X-RequestDigest'] = that.get_formDigest();
return (
await fetch(buildUrl(opt.url, that), ajaxOpts)
).json();
}
);
}
return (await fetch(buildUrl(opt.url, that), ajaxOpts)).json();
}
function failHandler(results, _textStatus, errorThrown) {
const log = window.console ? console.log : alert;
let response;
try {
const parsed = JSON.parse(results.responseText);
response = parsed.error.message.value;
} catch (e) {
response =
(results && results.responseText) || errorThrown.responseText;
}
log('Call failed. Error: ' + response);
}
function SPRest(appUrl, hostUrl) {
this[PRIVATE] = {
_formDigest: undefined,
_getContext: undefined,
// Using nometadata can result in errors: { status: "parsererror", message: "Invalid character" }
// Use 'verbose' instead if your version doesn't support.
_odataType: 'verbose',
};
this.appUrl = removeTrailingSlash(
appUrl || _spPageContextInfo.webAbsoluteUrl
);
this.hostUrl = hostUrl;
const isCurrentSite =
this.appUrl === _spPageContextInfo.webServerRelativeUrl ||
this.appUrl === _spPageContextInfo.webAbsoluteUrl;
// This will set the formDigest, if appUrl matches where the
// code is running. This will save an API call to contextinfo
// within `set_formDigest`
if (isCurrentSite) {
const digestEl = document.getElementById('__REQUESTDIGEST');
if (digestEl != null) {
this[PRIVATE]._formDigest = digestEl.value;
}
}
this[PRIVATE]._getContext = this.set_formDigest(this.get_formDigest());
}
SPRest.prototype = {
get_contextInfo: getContextInfo,
get_formDigest: getFormDigest,
set_formDigest: setFormDigest,
get_odataHeader: getOdataHeader,
get_odataType: getOdataType,
set_odataType: setOdataType,
get: get,
add: add,
update: update,
delete: deleteItem,
camlQuery: camlQuery,
failHandler: failHandler,
};
window.$sp = function (appUrl, hostUrl) {
return new SPRest(appUrl, hostUrl);
};
})();
/*!
* Created by Matthew Bramer
* Released under the MIT license
* Date: 2016-07-11
* Props to: http://blogs.msmvps.com/windsor/2015/02/13/reduce-code-need-for-rest-api-calls-with-sprestrepository/
* Tested using SharePoint Online & 2013 On-Prem.
*/
// http://sharepoint.stackexchange.com/questions/74978/can-i-tell-what-version-of-sharepoint-is-being-used-from-javascript
(function ($) {
'use strict';
var PRIVATE = 'DO_NOT_TOUCH_OR_YOU\'LL_BE_FIRED';
function getOdataHeader (odataType) {
return 'application/json;odata=' + this.get_odataType();
}
function getOdataType () {
// Using nometadata can result in errors: { status: "parsererror", message: "Invalid character" }
// Use 'verbose' instead if your version doesn't support.
return this[PRIVATE]._odataType;
}
function setOdataType (odataType) {
this[PRIVATE]._odataType = odataType;
}
function getFormDigest () {
return this[PRIVATE]._formDigest;
}
function setFormDigest (formDigest) {
var that = this;
if (!formDigest) {
// Returns a promise, so the constructor can queue up calls
// into the API.
return this.get_contextInfo().then(function (results) {
that[PRIVATE]._formDigest = results;
that[PRIVATE]._getContext = null;
return results;
});
} else {
this[PRIVATE]._formDigest = formDigest;
this[PRIVATE]._getContext = null;
}
}
function getContextInfo () {
return $.ajax({
type: 'POST',
url: this.appUrl + '/_api/contextinfo',
dataType: 'json',
headers: {
Accept: 'application/json;odata=verbose'
}
}).then(function (results) {
return results.d.GetContextWebInformation.FormDigestValue;
});
}
function get (options) {
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
var opt = $.extend({}, {
odataHeader: this.get_odataHeader()
}, options);
return $.ajax({
url: buildUrl(opt.url, this),
type: 'GET',
dataType: 'json',
headers: {
Accept: opt.odataHeader
}
});
}
function add (options) {
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
var ajaxOpts;
var that = this;
var opt = $.extend(true, {}, {
data: {},
odataHeader: this.get_odataHeader()
}, options);
ajaxOpts = {
url: buildUrl(opt.url, this),
type: 'POST',
data: padPayload(opt.data, opt.url, this.get_odataType()),
headers: {
Accept: opt.odataHeader,
"Content-Type": opt.odataHeader,
"X-RequestDigest": this.get_formDigest()
}
};
if (!this[PRIVATE]._getContext) {
return $.ajax(ajaxOpts);
} else {
return this[PRIVATE]._getContext.then(function () {
ajaxOpts.headers["X-RequestDigest"] = that.get_formDigest();
return $.ajax(ajaxOpts);
});
}
}
function update (options) {
var ajaxOpts;
var that = this;
var opt = $.extend(true, {}, {
data: {},
etag: '*',
httpMethod: 'PATCH',
odataHeader: this.get_odataHeader()
}, options);
ajaxOpts = {
url: buildUrl(opt.url, this),
type: 'POST',
data: padPayload(opt.data, opt.url, this.get_odataType()),
headers: {
Accept: opt.odataHeader,
"Content-Type": opt.odataHeader,
"IF-MATCH": opt.etag,
"X-Http-Method": opt.httpMethod,
"X-RequestDigest": this.get_formDigest()
}
};
if (!this[PRIVATE]._getContext) {
return $.ajax(ajaxOpts);
} else {
return this[PRIVATE]._getContext.then(function () {
ajaxOpts.headers["X-RequestDigest"] = that.get_formDigest();
return $.ajax(ajaxOpts);
});
}
}
function deleteItem (options) {
var ajaxOpts;
var that = this;
if (typeof options === 'string') {
var temp = options;
options = {};
options.url = temp;
}
var opt = $.extend({}, {
etag: '*',
odataHeader: this.get_odataHeader()
}, options);
ajaxOpts = {
url: buildUrl(opt.url, this),
type: 'DELETE',
headers: {
Accept: opt.odataHeader,
"IF-MATCH": opt.etag,
"X-RequestDigest": this.get_formDigest()
}
};
if (!this[PRIVATE]._getContext) {
return $.ajax(ajaxOpts);
} else {
return this[PRIVATE]._getContext.then(function () {
ajaxOpts.headers["X-RequestDigest"] = that.get_formDigest();
return $.ajax(ajaxOpts);
});
}
}
function camlQuery (options) {
var ajaxOpts;
var that = this;
var opt = $.extend({}, {
viewXml: '<View></View>',
odataHeader: this.get_odataHeader(),
formDigest: options.formDigest
}, options);
opt.data = {
query: {
ViewXml: opt.viewXml
}
};
ajaxOpts = {
type: 'POST',
url: buildUrl(opt.url, this),
data: padPayload(opt.data, opt.url, this.get_odataType()),
headers: {
Accept: opt.odataHeader,
"Content-Type": opt.odataHeader,
"X-RequestDigest": opt.formDigest
}
};
if (!this[PRIVATE]._getContext) {
return $.ajax(ajaxOpts);
} else {
return this[PRIVATE]._getContext.then(function () {
ajaxOpts.headers["X-RequestDigest"] = that.get_formDigest();
return $.ajax(ajaxOpts);
});
}
}
function failHandler (results, textStatus, errorThrown) {
var response,
log = (window.console) ? console.log : alert
;
try {
var parsed = JSON.parse(results.responseText);
response = parsed.error.message.value;
} catch (e) {
response = results && results.responseText || errorThrown.responseText;
}
log('Call failed. Error: ' + response);
}
// Utils
function removeTrailingSlash (v) {
if (v === '/') {
return v;
}
return v.replace(/\/$/, '');
}
function concatAppUrl (url, appUrl) {
if (url.indexOf(appUrl) === -1) {
url = (url.charAt(0) === '/') ? url : '/' + url;
url = appUrl + url;
}
return url;
}
function padPayload (data, url, odataType) {
// Determines what type of request is being sent.
// If it's verbose, then the payload needs to be
// padded with __metadata.
var isNotString = typeof data !== 'string';
var shouldAddMetadata = (odataType === 'verbose') ? true : false;
var isCamlQuery = (data.query && data.query.ViewXml) ? true : false;
if (isNotString) {
if (shouldAddMetadata) {
if (isCamlQuery) {
data.query.__metadata = {
type: 'SP.CamlQuery'
};
} else {
var rfindListName = /\('(.+)'\)/i;
var listName = url.match(rfindListName)[1];
data.__metadata = {
type: 'SP.Data.{0}ListItem'.replace('{0}', listName)
};
}
}
data = JSON.stringify(data);
}
return data;
}
function buildUrl (url, api) {
// TODO:
// Test this from an add-in.
url = concatAppUrl(url, api.appUrl);
if (api.hostUrl) {
var delimiter = '_api/';
var index = url.indexOf(delimiter);
url = url.slice(0, index + delimiter.length) +
'SP.AppContextSite(@target)' +
url.slice(index + delimiter.length - 1);
var connector = '?';
if (url.indexOf('?') > -1 && url.indexOf('$') > -1) {
connector = '&';
}
url = url + connector + "@target='" + api.hostUrl + "'";
}
return url;
}
function SPRest (appUrl, hostUrl) {
this[PRIVATE] = {
_formDigest: undefined,
_getContext: undefined,
// Using nometadata can result in errors: { status: "parsererror", message: "Invalid character" }
// Use 'verbose' instead if your version doesn't support.
_odataType: 'nometadata'
};
this.appUrl = removeTrailingSlash(appUrl || _spPageContextInfo.webAbsoluteUrl);
this.hostUrl = hostUrl;
if ((this.appUrl === _spPageContextInfo.webServerRelativeUrl) || (this.appUrl === _spPageContextInfo.webAbsoluteUrl)) {
this[PRIVATE]._formDigest = document.getElementById('__REQUESTDIGEST').value;
}
this[PRIVATE]._getContext = this.set_formDigest(this.get_formDigest());
}
SPRest.prototype = {
get_contextInfo: getContextInfo,
get_formDigest: getFormDigest,
set_formDigest: setFormDigest,
get_odataHeader: getOdataHeader,
get_odataType: getOdataType,
set_odataType: setOdataType,
get: get,
add: add,
update: update,
"delete": deleteItem,
camlQuery: camlQuery,
failHandler: failHandler
};
window.$sp = function (appUrl, hostUrl) {
return new SPRest(appUrl, hostUrl);
};
}(jQuery));
/*
* Usage
*
(function ($, window, undefined) {
var rest, rest2;
function initializeApi () {
var dfd = $.Deferred();
rest = $sp(); // defaults to current web.
rest.set_odataType('verbose');
rest2 = $sp('https://domain.tld/managedPath/siteName'); // use another web in another site collection or just another web.
return dfd.resolve().promise();
}
function getData () {
return $.when.apply($, [
sameSiteData(),
getCrossSiteData()
]);
}
function sameSiteData () {
return $.when.apply($, [
rest.update({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/Lists/getByTitle('Training')/items(2)",
data: {
Title: 'nadaadf-hey'
}
}),
rest.add({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('Training')/items"
}),
rest.get(_spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('Pages')/items")
]);
}
function getCrossSiteData () {
var now = (new Date()).toISOString();
return $.when.apply($, [
rest2.add({
url: "/_api/web/lists/getByTitle('Announcements')/items"
}),
rest2.update({
url: "/_api/web/Lists/getByTitle('Announcements')/items(1)",
data: {
Title: 'nadaadf-new one'
}
}),
rest2.get("/_api/web/lists/getByTitle('Announcements')/items"),
rest2.camlQuery({
url: "/_api/web/lists/getByTitle('Training')/GetItems",
viewXml: "<View><Query> \
<OrderBy><FieldRef Name='EndDate' /></OrderBy> \
<Where> \
<And> \
<Eq><FieldRef Name='PageId' /><Value Type='Lookup' LookupId='True'>3</Value></Eq> \
<Geq><FieldRef Name='EndDate' /><Value Type='DateTime'>" + now + "</Value></Geq> \
</And> \
</Where> \
</Query></View>"
})
]);
}
function deleteItem (api, results) {
var uri = results.d.__metadata.uri;
console.log('deleting:', uri);
return api.delete(uri);
}
function parseResults (sameSite, crossSite) {
var sameSiteData, crossSiteData;
var addedItemResults = crossSite[0][0];
var output = '';
try {
sameSiteData = sameSite[2][0].d.results;
crossSiteData = crossSite[2][0].d.results
} catch (e) {
return;
}
deleteItem(rest2, addedItemResults);
output = sameSiteData.map(function (item) {
return '<li>' + item.ID + '</li>';
}).join('');
output += crossSiteData.map(function (item) {
return '<li>' + item.ID + '</li>';
}).join('');
console.log(output);
}
function debug () {
console.log(arguments);
debugger;
}
initializeApi()
.then(getData)
.then(parseResults)
.fail(debug);
}(jQuery, window));
rest.get("/_api/web/lists/getByTitle('ConfigList')/items?$filter=Title%20eq%20%27heatgrade%27&$select=Title,JSON,Id").then(parseResults).fail(debug);
rest.get({
url: "/_api/web/lists/getByTitle('ConfigList')/items?$filter=Title%20eq%20%27heatgrade%27&$select=Title,JSON,Id",
odataHeader: 'application/json;odata=verbose'
}).then(parseResults).fail(debug);
rest.add({
url: "/_api/web/lists/getByTitle('ConfigList')/items",
data: {
Title: 'heatgrade',
JSON:'{"hot":{"grade":7,"color":"red"},"warm":{"grade":5,"color":"yellow"},"cold":{"grade":3,"color":"#88A4BC"}}'
}
});
rest.update({
url: "/_api/web/lists/getByTitle('ConfigList')/items(6)",
data: {
Title: 'heatgrade',
JSON:'{"hot":{"grade":7,"color":"red"},"warm":{"grade":5,"color":"yellow"},"cold":{"grade":3,"color":"#88A4BC"}}'
},
etag: '*',
formDigest: document.getElementById('__REQUESTDIGEST').value,
httpMethod: 'PATCH'
});
rest.update({
url: "/_api/web/lists/getByTitle('ConfigList')/items(6)",
data: {
Title: 'heatgrade',
JSON: JSON.stringify({
"hot": {
"grade": 7,
"color": "red"
},
"warm": {
"grade": 5,
"color": "yellow"
},
"cold": {
"grade": 3,
"color": "#88A4BC"
}
},
null, '\t')
}
});
rest.delete("/_api/web/lists/getByTitle('ConfigList')/items(9)");
rest.delete({
url: "/_api/web/lists/getByTitle('ConfigList')/items(6)"
});
// rest.get("/_api/web/lists/getByTitle('ConfigList')/items?$filter=Title%20eq%20%27heatgrade%27&$select=Title,JSON").then(debug).fail(debug);
// Get fresh FormDigest
rest.get_contextInfo().then(function(formDigest) { console.log(formDigest); });
// Query lists/libs using caml queries.
var now = (new Date()).toISOString();
rest.camlQuery({
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('Training')/GetItems"
viewXml: "<View><Query> \
<OrderBy><FieldRef Name='EndDate' /></OrderBy> \
<Where> \
<And> \
<Eq><FieldRef Name='PageId' /><Value Type='Lookup' LookupId='True'>2</Value></Eq> \
<Geq><FieldRef Name='EndDate' /><Value Type='DateTime'>" + now + "</Value></Geq> \
</And> \
</Where> \
</Query></View>"
});
// Create list view
const payload = JSON.stringify({
__metadata: { type: 'SP.View' },
Title: 'View Title',
ViewData: '<FieldRef Name="StaticName1" /><FieldRef Name="Author" />',
ViewQuery: '<OrderBy><FieldRef Name="StaticName1" /></OrderBy><Where></Where>'
});
rest.add({
url: "/_api/web/lists/getByTitle('ListName')/Views",
data: payload
});
// Create new field in list/lib
const fieldXml = `<Field Type="Text" DisplayName="Application" Required="FALSE" EnforceUniqueValues="FALSE" StaticName="Engagement_x0020_Request" Name="Engagement_x0020_Request" />`;
const payload = JSON.stringify({
parameters: {
__metadata: {
type: "SP.XmlSchemaFieldCreationInformation"
},
SchemaXml: fieldXml
}
});
// Make sure request is using the odata verbose setting.
// rest.set_odataType('verbose');
rest.add({
url: "/_api/web/lists/getByTitle('MyListName')/fields/createFieldAsXml",
data: payload
})
.then(console.log)
.catch(debug);
// Use $sp as a custom action and _spPageContextInfo isn't defined. Overcome this issue by waiting for the DOMContentLoaded event.
(function (window, $) {
let rest = null
function init () {
rest = $sp();
rest.get("/_api/web/lists").then(data => {
console.log(data);
debugger;
});
window.bramer = {
rest: rest
};
}
$(init);
}(window, jQuery));
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment