UNPKG

@pnp/sp

Version:

pnp - provides a fluent api for working with SharePoint REST

424 lines • 14.7 kB
import { combine, isUrlAbsolute, isArray, stringIsNullOrEmpty } from "@pnp/core"; import { Queryable, queryableFactory, op, get, post, patch, del } from "@pnp/queryable"; export const spInvokableFactory = (f) => { return queryableFactory(f); }; /** * SharePointQueryable Base Class * */ export class _SPQueryable extends Queryable { /** * Creates a new instance of the SharePointQueryable class * * @constructor * @param base A string or SharePointQueryable that should form the base part of the url * */ constructor(base, path) { if (typeof base === "string") { let url = ""; let parentUrl = ""; // we need to do some extra parsing to get the parent url correct if we are // being created from just a string. if (isUrlAbsolute(base) || base.lastIndexOf("/") < 0) { parentUrl = base; url = combine(base, path); } else if (base.lastIndexOf("/") > base.lastIndexOf("(")) { // .../items(19)/fields const index = base.lastIndexOf("/"); parentUrl = base.slice(0, index); path = combine(base.slice(index), path); url = combine(parentUrl, path); } else { // .../items(19) const index = base.lastIndexOf("("); parentUrl = base.slice(0, index); url = combine(base, path); } // init base with corrected string value super(url); this.parentUrl = parentUrl; } else { super(base, path); const q = isArray(base) ? base[0] : base; this.parentUrl = isArray(base) ? base[1] : q.toUrl(); } } /** * Gets the full url with query information */ toRequestUrl() { const aliasedParams = new URLSearchParams(this.query); // this regex is designed to locate aliased parameters within url paths let url = this.toUrl().replace(/'!(@.+?)::((?:[^']|'')+)'/ig, (match, labelName, value) => { this.log(`Rewriting aliased parameter from match ${match} to label: ${labelName} value: ${value}`, 0); aliasedParams.set(labelName, `'${value}'`); return labelName; }); const query = aliasedParams.toString(); if (!stringIsNullOrEmpty(query)) { url += `${url.indexOf("?") > -1 ? "&" : "?"}${query}`; } return url; } /** * Choose which fields to return * * @param selects One or more fields to return */ select(...selects) { if (selects.length > 0) { this.query.set("$select", selects.join(",")); } return this; } /** * Expands fields such as lookups to get additional data * * @param expands The Fields for which to expand the values */ expand(...expands) { if (expands.length > 0) { this.query.set("$expand", expands.join(",")); } return this; } /** * Gets a parent for this instance as specified * * @param factory The contructor for the class to create */ getParent(factory, path, base = this.parentUrl) { return factory([this, base], path); } } export const SPQueryable = spInvokableFactory(_SPQueryable); /** * Represents a REST collection which can be filtered, paged, and selected * */ export class _SPCollection extends _SPQueryable { /** * Filters the returned collection (https://msdn.microsoft.com/en-us/library/office/fp142385.aspx#bk_supported) * * @param filter The string representing the filter query */ filter(filter) { if (typeof filter === "object") { this.query.set("$filter", filter.toString()); return this; } if (typeof filter === "function") { this.query.set("$filter", filter(SPOData.Where()).toString()); return this; } this.query.set("$filter", filter.toString()); return this; } /** * Orders based on the supplied fields * * @param orderby The name of the field on which to sort * @param ascending If false DESC is appended, otherwise ASC (default) */ orderBy(orderBy, ascending = true) { const o = "$orderby"; const query = this.query.has(o) ? this.query.get(o).split(",") : []; query.push(`${orderBy} ${ascending ? "asc" : "desc"}`); this.query.set(o, query.join(",")); return this; } /** * Skips the specified number of items * * @param skip The number of items to skip */ skip(skip) { this.query.set("$skip", skip.toString()); return this; } /** * Limits the query to only return the specified number of items * * @param top The query row limit */ top(top) { this.query.set("$top", top.toString()); return this; } } export const SPCollection = spInvokableFactory(_SPCollection); /** * Represents an instance that can be selected * */ export class _SPInstance extends _SPQueryable { } export const SPInstance = spInvokableFactory(_SPInstance); /** * Adds the a delete method to the tagged class taking no parameters and calling spPostDelete */ export function deleteable() { return function () { return spPostDelete(this); }; } export function deleteableWithETag() { return function (eTag = "*") { return spPostDeleteETag(this, {}, eTag); }; } export const spGet = (o, init) => { return op(o, get, init); }; export const spPost = (o, init) => op(o, post, init); export const spPostMerge = (o, init) => { init = init || {}; init.headers = { ...init.headers, "X-HTTP-Method": "MERGE" }; return spPost(o, init); }; export const spPostDelete = (o, init) => { init = init || {}; init.headers = { ...init.headers || {}, "X-HTTP-Method": "DELETE" }; return spPost(o, init); }; export const spPostDeleteETag = (o, init, eTag = "*") => { init = init || {}; init.headers = { ...init.headers || {}, "IF-Match": eTag }; return spPostDelete(o, init); }; export const spDelete = (o, init) => op(o, del, init); export const spPatch = (o, init) => op(o, patch, init); var FilterOperation; (function (FilterOperation) { FilterOperation["Equals"] = "eq"; FilterOperation["NotEquals"] = "ne"; FilterOperation["GreaterThan"] = "gt"; FilterOperation["GreaterThanOrEqualTo"] = "ge"; FilterOperation["LessThan"] = "lt"; FilterOperation["LessThanOrEqualTo"] = "le"; FilterOperation["StartsWith"] = "startswith"; FilterOperation["SubstringOf"] = "substringof"; })(FilterOperation || (FilterOperation = {})); var FilterJoinOperator; (function (FilterJoinOperator) { FilterJoinOperator["And"] = "and"; FilterJoinOperator["AndWithSpace"] = " and "; FilterJoinOperator["Or"] = "or"; FilterJoinOperator["OrWithSpace"] = " or "; })(FilterJoinOperator || (FilterJoinOperator = {})); class SPOData { static Where() { return new InitialFieldQuery([]); } } // Linting complains that TBaseInterface is unused, but without it all the intellisense is lost since it's carrying it through the chain class BaseQuery { constructor(query) { this.query = []; this.query = query; } } class QueryableFields extends BaseQuery { constructor(q) { super(q); } text(internalName) { return new TextField([...this.query, internalName]); } choice(internalName) { return new TextField([...this.query, internalName]); } multiChoice(internalName) { return new TextField([...this.query, internalName]); } number(internalName) { return new NumberField([...this.query, internalName]); } date(internalName) { return new DateField([...this.query, internalName]); } boolean(internalName) { return new BooleanField([...this.query, internalName]); } lookup(internalName) { return new LookupQueryableFields([...this.query], internalName); } lookupId(internalName) { const col = internalName.endsWith("Id") ? internalName : `${internalName}Id`; return new NumberField([...this.query, col]); } } class QueryableAndResult extends QueryableFields { or(...queries) { return new ComparisonResult([...this.query, `(${queries.map(x => x.toString()).join(FilterJoinOperator.OrWithSpace)})`]); } } class QueryableOrResult extends QueryableFields { and(...queries) { return new ComparisonResult([...this.query, `(${queries.map(x => x.toString()).join(FilterJoinOperator.AndWithSpace)})`]); } } class InitialFieldQuery extends QueryableFields { or(...queries) { if (queries == null || queries.length === 0) { return new QueryableFields([...this.query, FilterJoinOperator.Or]); } return new ComparisonResult([...this.query, `(${queries.map(x => x.toString()).join(FilterJoinOperator.OrWithSpace)})`]); } and(...queries) { if (queries == null || queries.length === 0) { return new QueryableFields([...this.query, FilterJoinOperator.And]); } return new ComparisonResult([...this.query, `(${queries.map(x => x.toString()).join(FilterJoinOperator.AndWithSpace)})`]); } } class LookupQueryableFields extends BaseQuery { constructor(q, LookupField) { super(q); this.LookupField = LookupField; } Id(id) { return new ComparisonResult([...this.query, `${this.LookupField}/Id`, FilterOperation.Equals, id.toString()]); } text(internalName) { return new TextField([...this.query, `${this.LookupField}/${internalName}`]); } number(internalName) { return new NumberField([...this.query, `${this.LookupField}/${internalName}`]); } } class NullableField extends BaseQuery { constructor(q) { super(q); this.LastIndex = q.length - 1; this.InternalName = q[this.LastIndex]; } toODataValue(value) { return `'${value}'`; } isNull() { return new ComparisonResult([...this.query, FilterOperation.Equals, "null"]); } isNotNull() { return new ComparisonResult([...this.query, FilterOperation.NotEquals, "null"]); } } class ComparableField extends NullableField { equals(value) { return new ComparisonResult([...this.query, FilterOperation.Equals, this.toODataValue(value)]); } notEquals(value) { return new ComparisonResult([...this.query, FilterOperation.NotEquals, this.toODataValue(value)]); } in(...values) { return SPOData.Where().or(...values.map(x => this.equals(x))); } notIn(...values) { return SPOData.Where().and(...values.map(x => this.notEquals(x))); } } class TextField extends ComparableField { startsWith(value) { const filter = `${FilterOperation.StartsWith}(${this.InternalName}, ${this.toODataValue(value)})`; this.query[this.LastIndex] = filter; return new ComparisonResult([...this.query]); } contains(value) { const filter = `${FilterOperation.SubstringOf}(${this.toODataValue(value)}, ${this.InternalName})`; this.query[this.LastIndex] = filter; return new ComparisonResult([...this.query]); } } class BooleanField extends NullableField { toODataValue(value) { return `${value == null ? "null" : value ? 1 : 0}`; } isTrue() { return new ComparisonResult([...this.query, FilterOperation.Equals, this.toODataValue(true)]); } isFalse() { return new ComparisonResult([...this.query, FilterOperation.Equals, this.toODataValue(false)]); } isFalseOrNull() { const filter = `(${[ this.InternalName, FilterOperation.Equals, this.toODataValue(null), FilterJoinOperator.Or, this.InternalName, FilterOperation.Equals, this.toODataValue(false), ].join(" ")})`; this.query[this.LastIndex] = filter; return new ComparisonResult([...this.query]); } } class NumericField extends ComparableField { greaterThan(value) { return new ComparisonResult([...this.query, FilterOperation.GreaterThan, this.toODataValue(value)]); } greaterThanOrEquals(value) { return new ComparisonResult([...this.query, FilterOperation.GreaterThanOrEqualTo, this.toODataValue(value)]); } lessThan(value) { return new ComparisonResult([...this.query, FilterOperation.LessThan, this.toODataValue(value)]); } lessThanOrEquals(value) { return new ComparisonResult([...this.query, FilterOperation.LessThanOrEqualTo, this.toODataValue(value)]); } } class NumberField extends NumericField { toODataValue(value) { return `${value}`; } } class DateField extends NumericField { toODataValue(value) { return `'${value.toISOString()}'`; } isBetween(startDate, endDate) { const filter = `(${[ this.InternalName, FilterOperation.GreaterThan, this.toODataValue(startDate), FilterJoinOperator.And, this.InternalName, FilterOperation.LessThan, this.toODataValue(endDate), ].join(" ")})`; this.query[this.LastIndex] = filter; return new ComparisonResult([...this.query]); } isToday() { const StartToday = new Date(); StartToday.setHours(0, 0, 0, 0); const EndToday = new Date(); EndToday.setHours(23, 59, 59, 999); return this.isBetween(StartToday, EndToday); } } class ComparisonResult extends BaseQuery { // eslint-disable-next-line max-len and(...queries) { if (queries == null || queries.length === 0) { return new QueryableAndResult([...this.query, FilterJoinOperator.And]); } return new ComparisonResult([...this.query, FilterJoinOperator.And, `(${queries.map(x => x.toString()).join(FilterJoinOperator.AndWithSpace)})`]); } // eslint-disable-next-line max-len or(...queries) { if (queries == null || queries.length === 0) { return new QueryableOrResult([...this.query, FilterJoinOperator.Or]); } return new ComparisonResult([...this.query, FilterJoinOperator.Or, `(${queries.map(x => x.toString()).join(FilterJoinOperator.OrWithSpace)})`]); } toString() { return this.query.join(" "); } } //# sourceMappingURL=spqueryable.js.map