From 4347f30a77cbfe231d1e919c70ab23f39e2c8fcd Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Wed, 17 Oct 2018 20:20:05 +0100 Subject: [PATCH 1/8] Integrated queryableprop into sharepointqueryable --- packages/graph/src/teams.ts | 3 +- packages/odata/src/odata.ts | 1 + packages/odata/src/queryable.ts | 17 ++- packages/odata/src/queryableprop.ts | 128 ++++++++++++++++++ packages/sp/src/attachmentfiles.ts | 6 +- packages/sp/src/sharepointqueryable.ts | 104 ++++++++------ .../sp/src/sharepointqueryablesecurable.ts | 2 +- .../sp/src/sharepointqueryableshareable.ts | 6 +- 8 files changed, 213 insertions(+), 54 deletions(-) create mode 100644 packages/odata/src/queryableprop.ts diff --git a/packages/graph/src/teams.ts b/packages/graph/src/teams.ts index ba97b3928..f00362862 100644 --- a/packages/graph/src/teams.ts +++ b/packages/graph/src/teams.ts @@ -21,6 +21,7 @@ import { import { ODataParser, ODataDefaultParser, + QueryableGet, } from "@pnp/odata"; import { @@ -81,7 +82,7 @@ export class Team extends GraphQueryableInstance { * @param parser Allows you to specify a parser to handle the result * @param getOptions The options used for this request */ - public get(parser: ODataParser = new ODataDefaultParser(), options: FetchOptions = {}): Promise { + public get(parser: ODataParser> = new ODataDefaultParser(), options: FetchOptions = {}): Promise> { return this.clone(Team, "").setEndpoint(GraphEndpoints.Beta).getCore(parser, options); } } diff --git a/packages/odata/src/odata.ts b/packages/odata/src/odata.ts index 1adb72bb7..230422f32 100644 --- a/packages/odata/src/odata.ts +++ b/packages/odata/src/odata.ts @@ -3,3 +3,4 @@ export * from "./parsers"; export * from "./pipeline"; export * from "./queryable"; export * from "./odatabatch"; +export * from "./queryableprop"; \ No newline at end of file diff --git a/packages/odata/src/queryable.ts b/packages/odata/src/queryable.ts index f95e2d784..1a4241829 100644 --- a/packages/odata/src/queryable.ts +++ b/packages/odata/src/queryable.ts @@ -14,8 +14,11 @@ import { getDefaultPipeline, pipe, } from "./pipeline"; +import { + QueryableGet +} from './queryableprop'; -export abstract class Queryable { +export abstract class Queryable { /** * Additional options to be set before sending actual http request @@ -123,8 +126,8 @@ export abstract class Queryable { return this; } - protected getCore(parser: ODataParser = new JSONParser(), options: FetchOptions = {}): Promise { - return this.toRequestContext("GET", options, parser, getDefaultPipeline()).then(context => pipe(context)); + protected getCore(parser: ODataParser> = new JSONParser(), options: FetchOptions = {}): Promise> { + return this.toRequestContext>("GET", options, parser, getDefaultPipeline()).then(context => pipe(context)); } protected postCore(options: FetchOptions = {}, parser: ODataParser = new JSONParser()): Promise { @@ -187,7 +190,7 @@ export abstract class Queryable { pipeline: Array<(c: RequestContext) => Promise>>): Promise>; } -export abstract class ODataQueryable extends Queryable { +export abstract class ODataQueryable extends Queryable { /** * Tracks the batch of which this query may be part @@ -235,12 +238,12 @@ export abstract class ODataQueryable(parser: ODataParser = new ODataDefaultParser(), options: FetchOptions = {}): Promise { + public get(parser: ODataParser> = new ODataDefaultParser(), options: FetchOptions = {}): Promise> { return this.getCore(parser, options); } - protected getCore(parser: ODataParser = new ODataDefaultParser(), options: FetchOptions = {}): Promise { - return this.toRequestContext("GET", options, parser, getDefaultPipeline()).then(context => pipe(context)); + protected getCore(parser: ODataParser> = new ODataDefaultParser(), options: FetchOptions = {}): Promise> { + return this.toRequestContext>("GET", options, parser, getDefaultPipeline()).then(context => pipe(context)); } protected postCore(options: FetchOptions = {}, parser: ODataParser = new ODataDefaultParser()): Promise { diff --git a/packages/odata/src/queryableprop.ts b/packages/odata/src/queryableprop.ts new file mode 100644 index 000000000..26a583f77 --- /dev/null +++ b/packages/odata/src/queryableprop.ts @@ -0,0 +1,128 @@ +export interface QueryableInterface { + readonly queryableInterface: unique symbol; +} + +export interface QueryableProp< + PropType, + Expandable extends boolean, + DefaultSelected extends boolean, + Selected extends boolean, + Expanded extends boolean> { + readonly symbol: unique symbol; + readonly propType: PropType; + readonly expandable: Expandable; + readonly defaultSelected: DefaultSelected; + readonly selected: Selected; + readonly expanded: Expanded; +} + +export interface QueryableCompositeProp< + PropType, + ParentProp extends string, + ChildProp extends string, + Selected extends boolean, + Expanded extends boolean> { + readonly symbol: unique symbol; + readonly propType: PropType; + readonly parentProp: ParentProp; + readonly childProp: ChildProp; + readonly selected: Selected; + readonly expanded: Expanded; +} + +export type QueryableSelectableKeys = T extends (infer A)[] ? QueryableSelectableKeysImpl : QueryableSelectableKeysImpl; +type QueryableSelectableKeysImpl = { + [U in keyof T]: + T[U] extends QueryableCompositeProp ? + never : + U; +}[keyof T]; + +export type QueryableSelect> = T extends (infer A)[] ? QueryableSelectImpl>[] : QueryableSelectImpl; +type QueryableSelectImpl> = T extends QueryableInterface ? { + [U in keyof T]: + U extends K ? + T[U] extends QueryableProp ? + QueryableProp : + T[U] extends QueryableCompositeProp ? + QueryableCompositeProp : + T[U] : + T[U]; +} : T; + +export type QueryableExpandableKeys = T extends (infer A)[] ? QueryableExpandableKeysImpl : QueryableExpandableKeysImpl; +type QueryableExpandableKeysImpl = { + [U in keyof T]: + T[U] extends QueryableProp ? + never: + T[U] extends QueryableCompositeProp ? + never : + U; +}[keyof T]; + +export type QueryableExpand> = T extends (infer A)[] ? QueryableExpandImpl>[] : QueryableExpandImpl; +type QueryableExpandImpl> = T extends QueryableInterface ? { + [U in keyof T]: + U extends K ? + T[U] extends QueryableProp ? + QueryableProp : + T[U] : + T[U] extends QueryableCompositeProp ? + ParentProp extends K ? + QueryableCompositeProp : + T[U] : + T[U]; +} : T; + +export type QueryableSelectedProps = T extends (infer A)[] ? QueryableSelectedPropsImpl : QueryableSelectedPropsImpl; +type QueryableSelectedPropsImpl = { + [U in keyof T]: + U extends string ? + T[U] extends QueryableProp ? + U : + T[U] extends QueryableProp ? + U : + T[U] extends QueryableCompositeProp ? + never : + U : + never; +}[keyof T]; + +export type QueryableSelectedCompositeChildProps = T extends (infer A)[] ? + QueryableSelectedCompositeChildPropsImpl : + QueryableSelectedCompositeChildPropsImpl; +type QueryableSelectedCompositeChildPropsImpl = { + [U in keyof T]: + T[U] extends QueryableCompositeProp ? + ChildProp : + never; +}[keyof T]; + +export type QueryableCompositePropType = T extends (infer A)[] ? + QueryableCompositePropTypeImpl : + QueryableCompositePropTypeImpl; +type QueryableCompositePropTypeImpl = { + [U in keyof T]: + T[U] extends QueryableCompositeProp ? + PropType : + never; +}[keyof T]; + +export type QueryableGet = + T extends QueryableInterface ? + (T extends (infer A)[] ? QueryableGetImpl[] : QueryableGetImpl) : + T; +type QueryableGetImpl = { + [U in QueryableSelectedProps]: + T[U] extends QueryableProp ? + PropType : + T[U] extends QueryableProp ? + PropType : + T[U] extends QueryableProp ? + { + [V in QueryableSelectedCompositeChildProps]: QueryableCompositePropType; + } : + T[U] +}; + +export type QueryableOrderableKeys = T extends (infer A)[] ? keyof A : keyof T; diff --git a/packages/sp/src/attachmentfiles.ts b/packages/sp/src/attachmentfiles.ts index 1d817e4d2..4c0cb94eb 100644 --- a/packages/sp/src/attachmentfiles.ts +++ b/packages/sp/src/attachmentfiles.ts @@ -1,5 +1,5 @@ import { SharePointQueryableInstance, SharePointQueryableCollection, defaultPath } from "./sharepointqueryable"; -import { TextParser, BlobParser, JSONParser, BufferParser, ODataParser } from "@pnp/odata"; +import { TextParser, BlobParser, JSONParser, BufferParser, ODataParser, QueryableGet } from "@pnp/odata"; export interface AttachmentFileInfo { name: string; @@ -131,8 +131,8 @@ export class AttachmentFile extends SharePointQueryableInstance { // }); // } - private getParsed(parser: ODataParser): Promise { - return this.clone(AttachmentFile, "$value", false).get(parser); + private getParsed(parser: ODataParser>): Promise> { + return this.clone(AttachmentFile, "$value", false).get(parser); } } diff --git a/packages/sp/src/sharepointqueryable.ts b/packages/sp/src/sharepointqueryable.ts index ce59f441c..845bd987a 100644 --- a/packages/sp/src/sharepointqueryable.ts +++ b/packages/sp/src/sharepointqueryable.ts @@ -11,6 +11,12 @@ import { ODataParser, ODataQueryable, RequestContext, + QueryableGet, + QueryableSelectableKeys, + QueryableSelect, + QueryableExpandableKeys, + QueryableExpand, + QueryableOrderableKeys, } from "@pnp/odata"; import { Logger, LogLevel } from "@pnp/logging"; import { SPBatch } from "./batch"; @@ -19,25 +25,24 @@ import { toAbsoluteUrl } from "./utils/toabsoluteurl"; import { metadata } from "./utils/metadata"; export interface SharePointQueryableConstructor { - new(baseUrl: string | SharePointQueryable, path?: string): T; + new(baseUrl: string | SharePointQueryableBase, path?: string): T; } /** * SharePointQueryable Base Class * */ -export class SharePointQueryable extends ODataQueryable { - +export class SharePointQueryableBase extends ODataQueryable { protected _forceCaching: boolean; /** - * Creates a new instance of the SharePointQueryable class + * Creates a new instance of the SharePointQueryableBase class * * @constructor - * @param baseUrl A string or SharePointQueryable that should form the base part of the url + * @param baseUrl A string or SharePointQueryableBase that should form the base part of the url * */ - constructor(baseUrl: string | SharePointQueryable, path?: string) { + constructor(baseUrl: string | SharePointQueryableBase, path?: string) { super(); this._forceCaching = false; @@ -73,7 +78,7 @@ export class SharePointQueryable extends ODataQueryable(factory: SharePointQueryableConstructor): T { const o = new factory(this._url, null); @@ -102,38 +107,14 @@ export class SharePointQueryable extends ODataQueryable 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 - */ - public expand(...expands: string[]): this { - 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 */ - protected getParent( + protected getParent( factory: SharePointQueryableConstructor, - baseUrl: string | SharePointQueryable = this.parentUrl, + baseUrl: string | SharePointQueryableBase = this.parentUrl, path?: string, batch?: SPBatch): T { @@ -150,12 +131,12 @@ export class SharePointQueryable extends ODataQueryable(factory: SharePointQueryableConstructor, additionalPath?: string, includeBatch = true): T { + protected clone(factory: SharePointQueryableConstructor, additionalPath?: string, includeBatch = true): T { let clone = new factory(this, additionalPath).configureFrom(this); const t = "@target"; if (this.query.has(t)) { @@ -207,12 +188,57 @@ export class SharePointQueryable extends ODataQueryable extends SharePointQueryableBase { + + + /** + * Choose which fields to return + * + * @param selects One or more fields to return + */ + public select>(...selects: K[]): SharePointQueryable> { + if (selects.length > 0) { + this.query.set("$select", selects.join(",")); + } + return this as any; + } + + /** + * Expands fields such as lookups to get additional data + * + * @param expands The Fields for which to expand the values + */ + public expand>(...expands: K[]): SharePointQueryable> { + if (expands.length > 0) { + this.query.set("$expand", expands.join(",")); + } + return this as any; + } +} /** * Represents a REST collection which can be filtered, paged, and selected * */ -export class SharePointQueryableCollection extends SharePointQueryable { +export class SharePointQueryableCollection extends SharePointQueryable { + + /** + * Choose which fields to return + * + * @param selects One or more fields to return + */ + public select>(...selects: K[]): SharePointQueryableCollection> { + return super.select(...selects) as any; + } + + /** + * Expands fields such as lookups to get additional data + * + * @param expands The Fields for which to expand the values + */ + public expand>(...expands: K[]): SharePointQueryableCollection> { + return super.expand(...expands) as any; + } /** * Filters the returned collection (https://msdn.microsoft.com/en-us/library/office/fp142385.aspx#bk_supported) @@ -230,7 +256,7 @@ export class SharePointQueryableCollection extends SharePointQu * @param orderby The name of the field on which to sort * @param ascending If false DESC is appended, otherwise ASC (default) */ - public orderBy(orderBy: string, ascending = true): this { + public orderBy(orderBy: QueryableOrderableKeys, ascending = true): this { const o = "$orderby"; const query = this.query.has(o) ? this.query.get(o).split(",") : []; query.push(`${orderBy} ${ascending ? "asc" : "desc"}`); @@ -263,7 +289,7 @@ export class SharePointQueryableCollection extends SharePointQu * Represents an instance that can be selected * */ -export class SharePointQueryableInstance extends SharePointQueryable { +export class SharePointQueryableInstance extends SharePointQueryable { /** * Curries the update function into the common pieces @@ -308,7 +334,7 @@ export class SharePointQueryableInstance extends SharePointQueryable { } /** - * Decorator used to specify the default path for SharePointQueryable objects + * Decorator used to specify the default path for SharePointQueryableBase objects * * @param path */ diff --git a/packages/sp/src/sharepointqueryablesecurable.ts b/packages/sp/src/sharepointqueryablesecurable.ts index 790f53279..d94420daa 100644 --- a/packages/sp/src/sharepointqueryablesecurable.ts +++ b/packages/sp/src/sharepointqueryablesecurable.ts @@ -3,7 +3,7 @@ import { BasePermissions, PermissionKind } from "./types"; import { SharePointQueryable, SharePointQueryableInstance } from "./sharepointqueryable"; import { hOP } from "@pnp/common"; -export class SharePointQueryableSecurable extends SharePointQueryableInstance { +export class SharePointQueryableSecurable extends SharePointQueryableInstance { /** * Gets the set of role assignments for this item diff --git a/packages/sp/src/sharepointqueryableshareable.ts b/packages/sp/src/sharepointqueryableshareable.ts index 427dff82d..d7d5bb28a 100644 --- a/packages/sp/src/sharepointqueryableshareable.ts +++ b/packages/sp/src/sharepointqueryableshareable.ts @@ -25,7 +25,7 @@ import { extractWebUrl } from "./utils/extractweburl"; /** * Internal helper class used to augment classes to include sharing functionality */ -export class SharePointQueryableShareable extends SharePointQueryable { +export class SharePointQueryableShareable extends SharePointQueryable { /** * Gets a sharing link for the supplied @@ -296,7 +296,7 @@ export class SharePointQueryableShareable extends SharePointQueryable { } } -export class SharePointQueryableShareableWeb extends SharePointQueryableSecurable { +export class SharePointQueryableShareableWeb extends SharePointQueryableSecurable { /** * Shares this web with the supplied users @@ -370,7 +370,7 @@ export class SharePointQueryableShareableWeb extends SharePointQueryableSecurabl } } -export class SharePointQueryableShareableItem extends SharePointQueryableSecurable { +export class SharePointQueryableShareableItem extends SharePointQueryableSecurable { /** * Gets a link suitable for sharing for this item From 04d58f022cf988ba7a306287df14356853acbd25 Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Thu, 18 Oct 2018 17:31:29 +0100 Subject: [PATCH 2/8] Added 'createDefaultAssociatedGroups' to Web class --- packages/sp/src/webs.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/sp/src/webs.ts b/packages/sp/src/webs.ts index 9fa47b7c6..86691440e 100644 --- a/packages/sp/src/webs.ts +++ b/packages/sp/src/webs.ts @@ -574,6 +574,14 @@ export class Web extends SharePointQueryableShareableWeb { public addClientSidePageByPath(pageName: string, listRelativePath: string, title = pageName.replace(/\.[^/.]+$/, "")): Promise { return ClientSidePage.create(this.getList(listRelativePath), pageName, title); } + + /** + * Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site + * + */ + public createDefaultAssociatedGroups(): Promise { + return this.clone(Web, `createDefaultAssociatedGroups`).postCore(); + } } /** From 987dc3b8e5bf0cc00e7e143560e6b581e7e759b9 Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Thu, 18 Oct 2018 20:35:52 +0100 Subject: [PATCH 3/8] Added support for default selected composite props --- packages/odata/src/queryableprop.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/odata/src/queryableprop.ts b/packages/odata/src/queryableprop.ts index 26a583f77..96cf93ff9 100644 --- a/packages/odata/src/queryableprop.ts +++ b/packages/odata/src/queryableprop.ts @@ -20,12 +20,14 @@ export interface QueryableCompositeProp< PropType, ParentProp extends string, ChildProp extends string, + DefaultSelected extends boolean, Selected extends boolean, Expanded extends boolean> { readonly symbol: unique symbol; readonly propType: PropType; readonly parentProp: ParentProp; readonly childProp: ChildProp; + readonly defaultSelected: DefaultSelected; readonly selected: Selected; readonly expanded: Expanded; } @@ -33,7 +35,7 @@ export interface QueryableCompositeProp< export type QueryableSelectableKeys = T extends (infer A)[] ? QueryableSelectableKeysImpl : QueryableSelectableKeysImpl; type QueryableSelectableKeysImpl = { [U in keyof T]: - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? never : U; }[keyof T]; @@ -44,8 +46,8 @@ type QueryableSelectImpl> = T extends Qu U extends K ? T[U] extends QueryableProp ? QueryableProp : - T[U] extends QueryableCompositeProp ? - QueryableCompositeProp : + T[U] extends QueryableCompositeProp ? + QueryableCompositeProp : T[U] : T[U]; } : T; @@ -55,7 +57,7 @@ type QueryableExpandableKeysImpl = { [U in keyof T]: T[U] extends QueryableProp ? never: - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? never : U; }[keyof T]; @@ -67,9 +69,9 @@ type QueryableExpandImpl> = T extends Qu T[U] extends QueryableProp ? QueryableProp : T[U] : - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? ParentProp extends K ? - QueryableCompositeProp : + QueryableCompositeProp : T[U] : T[U]; } : T; @@ -82,7 +84,7 @@ type QueryableSelectedPropsImpl = { U : T[U] extends QueryableProp ? U : - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? never : U : never; @@ -93,9 +95,11 @@ export type QueryableSelectedCompositeChildProps = QueryableSelectedCompositeChildPropsImpl; type QueryableSelectedCompositeChildPropsImpl = { [U in keyof T]: - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? ChildProp : - never; + T[U] extends QueryableCompositeProp ? + ChildProp : + never; }[keyof T]; export type QueryableCompositePropType = T extends (infer A)[] ? @@ -103,7 +107,7 @@ export type QueryableCompositePropType; type QueryableCompositePropTypeImpl = { [U in keyof T]: - T[U] extends QueryableCompositeProp ? + T[U] extends QueryableCompositeProp ? PropType : never; }[keyof T]; @@ -118,10 +122,10 @@ type QueryableGetImpl = { PropType : T[U] extends QueryableProp ? PropType : - T[U] extends QueryableProp ? + T[U] extends QueryableProp ? { [V in QueryableSelectedCompositeChildProps]: QueryableCompositePropType; - } : + } & PropType : T[U] }; From 38961ebfca583d2da6434966d6028d52052bbd6a Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Thu, 18 Oct 2018 20:51:18 +0100 Subject: [PATCH 4/8] Don't select symbols --- packages/odata/src/queryableprop.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/odata/src/queryableprop.ts b/packages/odata/src/queryableprop.ts index 96cf93ff9..d279f48b6 100644 --- a/packages/odata/src/queryableprop.ts +++ b/packages/odata/src/queryableprop.ts @@ -37,7 +37,9 @@ type QueryableSelectableKeysImpl = { [U in keyof T]: T[U] extends QueryableCompositeProp ? never : - U; + T[U] extends symbol ? + never : + U; }[keyof T]; export type QueryableSelect> = T extends (infer A)[] ? QueryableSelectImpl>[] : QueryableSelectImpl; @@ -59,7 +61,9 @@ type QueryableExpandableKeysImpl = { never: T[U] extends QueryableCompositeProp ? never : - U; + T[U] extends symbol ? + never : + U; }[keyof T]; export type QueryableExpand> = T extends (infer A)[] ? QueryableExpandImpl>[] : QueryableExpandImpl; @@ -86,7 +90,9 @@ type QueryableSelectedPropsImpl = { U : T[U] extends QueryableCompositeProp ? never : - U : + T[U] extends symbol ? + never : + U : never; }[keyof T]; From b93aa3460203a446999181131ad34cfe0e046e55 Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Thu, 18 Oct 2018 20:58:41 +0100 Subject: [PATCH 5/8] Added WebQueryableInterface to Web and Webs --- packages/sp/src/webs.ts | 123 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 4 deletions(-) diff --git a/packages/sp/src/webs.ts b/packages/sp/src/webs.ts index 86691440e..2341499da 100644 --- a/packages/sp/src/webs.ts +++ b/packages/sp/src/webs.ts @@ -1,4 +1,5 @@ import { extend, TypedHash, jsS } from "@pnp/common"; +import { QueryableInterface, QueryableProp, QueryableCompositeProp } from "@pnp/odata"; import { SharePointQueryableCollection, defaultPath } from "./sharepointqueryable"; import { SharePointQueryableShareableWeb } from "./sharepointqueryableshareable"; import { Folders, Folder } from "./folders"; @@ -11,7 +12,7 @@ import { ContentTypes } from "./contenttypes"; import { RoleDefinitions } from "./roles"; import { File } from "./files"; import { extractWebUrl } from "./utils/extractweburl"; -import { ChangeQuery, StorageEntity } from "./types"; +import { ChangeQuery, StorageEntity, PrincipalType } from "./types"; import { SiteUsers, SiteUser, CurrentUser, SiteUserProps } from "./siteusers"; import { UserCustomActions } from "./usercustomactions"; import { odataUrlFrom } from "./odata"; @@ -27,7 +28,7 @@ import { ClientSidePage, ClientSidePageComponent } from "./clientsidepages"; * */ @defaultPath("webs") -export class Webs extends SharePointQueryableCollection { +export class Webs extends SharePointQueryableCollection { /** * Adds a new web to the collection @@ -84,7 +85,7 @@ export class WebInfos extends SharePointQueryableCollection {} * */ @defaultPath("_api/web") -export class Web extends SharePointQueryableShareableWeb { +export class Web extends SharePointQueryableShareableWeb { /** * Creates a new web instance from the given url by indexing the location of the /_api/ @@ -110,7 +111,7 @@ export class Web extends SharePointQueryableShareableWeb { * */ public getParentWeb(): Promise<{ data: any; web: Web }> { - return this.select("ParentWeb/Id").expand("ParentWeb").get() + return this.expand("ParentWeb").select("ParentWeb/Id").get() .then(({ ParentWeb }) => new Site(this.toUrlAndQuery().split("/_api")[0]).openWebById(ParentWeb.Id)); } @@ -619,3 +620,117 @@ export interface WebEnsureUserResult { data: SiteUserProps; user: SiteUser; } + +export interface WebQueryableInterface extends QueryableInterface { + Alerts: QueryableProp; + AllowAutomaticASPXPageIndexing: QueryableProp; + AllowCreateDeclarativeWorkflowForCurrentUser: QueryableProp; + AllowDesignerForCurrentUser: QueryableProp; + AllowMasterPageEditingForCurrentUser: QueryableProp; + AllowRevertFromTemplateForCurrentUser: QueryableProp; + AllowRssFeeds: QueryableProp; + AllowSaveDeclarativeWorkflowAsTemplateForCurrentUser: QueryableProp; + AllowSavePublishDeclarativeWorkflowForCurrentUser: QueryableProp; + AllProperties: QueryableProp; + AlternateCssUrl: QueryableProp; + AppInstanceId: QueryableProp; + AssociatedMemberGroup: QueryableProp; + AssociatedOwnerGroup: QueryableProp; + AssociatedVisitorGroup: QueryableProp; + Author: QueryableProp; + AvailableContentTypes: QueryableProp; + AvailableFields: QueryableProp; + Configuration: QueryableProp; + ContentTypes: QueryableProp; + Created: QueryableProp; + CurrentChangeToken: QueryableProp<{StringValue: string}, false, true, false, false>; + CurrentUser: QueryableProp<{}, true, false, false, false>; + "CurrentUser/Email": QueryableCompositeProp; + "CurrentUser/Id": QueryableCompositeProp; + "CurrentUser/IsEmailAuthenticationGuestUser": QueryableCompositeProp; + "CurrentUser/IsHiddenInUI": QueryableCompositeProp; + "CurrentUser/IsShareByEmailGuestUser": QueryableCompositeProp; + "CurrentUser/IsSiteAdmin": QueryableCompositeProp; + "CurrentUser/LoginName": QueryableCompositeProp; + "CurrentUser/PrincipalType": QueryableCompositeProp; + "CurrentUser/Title": QueryableCompositeProp; + "CurrentUser/UserId": QueryableCompositeProp<{ NameId: string, NameIdIssuer: string }, "CurrentUser", "UserId", true, false, false>; + CustomMasterUrl: QueryableProp; + Description: QueryableProp; + DescriptionResource: QueryableProp; + DesignerDownloadUrlForCurrentUser: QueryableProp; + DesignPackageId: QueryableProp; + DocumentLibraryCalloutOfficeWebAppPreviewersDisabled: QueryableProp; + EffectiveBasePermissions: QueryableProp<{High: string; Low: string}, false, false, false, false>; + EnableMinimalDownload: QueryableProp; + EventReceivers: QueryableProp; + ExcludeFromOfflineClient: QueryableProp; + Features: QueryableProp; + Fields: QueryableProp; + FirstUniqueAncestorSecurableObject: QueryableProp; + Folders: QueryableProp; + FooterEnabled: QueryableProp; + HasUniqueRoleAssignments: QueryableProp; + HeaderEmphasis: QueryableProp; + HeaderLayout: QueryableProp; + HorizontalQuickLaunch: QueryableProp; + Id: QueryableProp; + IsMultilingual: QueryableProp; + Language: QueryableProp; + LastItemModifiedDate: QueryableProp; + LastItemUserModifiedDate: QueryableProp; + Lists: QueryableProp; + ListTemplates: QueryableProp; + MasterUrl: QueryableProp; + MegaMenuEnabled: QueryableProp; + Navigation: QueryableProp; + NoCrawl: QueryableProp; + ObjectCacheEnabled: QueryableProp; + OverwriteTranslationsOnChange: QueryableProp; + ParentWeb: QueryableProp; + "ParentWeb/Configuration": QueryableCompositeProp; + "ParentWeb/Created": QueryableCompositeProp; + "ParentWeb/Description": QueryableCompositeProp; + "ParentWeb/Id": QueryableCompositeProp; + "ParentWeb/Language": QueryableCompositeProp; + "ParentWeb/LastItemModifiedDate": QueryableCompositeProp; + "ParentWeb/LastItemUserModifiedDate": QueryableCompositeProp; + "ParentWeb/ServerRelativeUrl": QueryableCompositeProp; + "ParentWeb/Title": QueryableCompositeProp; + "ParentWeb/WebTemplate": QueryableCompositeProp; + "ParentWeb/WebTemplateId": QueryableCompositeProp; + PushNotificationSubscribers: QueryableProp; + QuickLaunchEnabled: QueryableProp; + RecycleBin: QueryableProp; + RecycleBinEnabled: QueryableProp; + RegionalSettings: QueryableProp; + RequestAccessEmail: QueryableProp; + ResourcePath: QueryableProp<{DecodedUrl: string}, false, true, false, false>; + RoleAssignments: QueryableProp; + RoleDefinitions: QueryableProp; + RootFolder: QueryableProp; + SaveSiteAsTemplateEnabled: QueryableProp; + ServerRelativeUrl: QueryableProp; + ShowUrlStructureForCurrentUser: QueryableProp; + SiteGroups: QueryableProp; + SiteLogoDescription: QueryableProp; + SiteLogoUrl: QueryableProp; + SiteUserInfoList: QueryableProp; + SiteUsers: QueryableProp; + SupportedUILanguageIds: QueryableProp; + SyndicationEnabled: QueryableProp; + ThemedCssFolderUrl: QueryableProp; + ThemeInfo: QueryableProp; + Title: QueryableProp; + TitleResource: QueryableProp; + TreeViewEnabled: QueryableProp; + UIVersion: QueryableProp; + UIVersionConfigurationEnabled: QueryableProp; + Url: QueryableProp; + UserCustomActions: QueryableProp; + Webs: QueryableProp; + WebTemplate: QueryableProp; + WelcomePage: QueryableProp; + WorkflowAssociations: QueryableProp; + WorkflowTemplates: QueryableProp; +} From 135207a40df6e361bf4aa167e20d4988c76a6896 Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Sun, 21 Oct 2018 11:30:32 +0100 Subject: [PATCH 6/8] Changed queryable interface to better represent odata calls --- packages/odata/src/queryableprop.ts | 250 +++++++++++++--------- packages/sp/src/webs.ts | 320 ++++++++++++++++++---------- 2 files changed, 355 insertions(+), 215 deletions(-) diff --git a/packages/odata/src/queryableprop.ts b/packages/odata/src/queryableprop.ts index d279f48b6..1a862ed18 100644 --- a/packages/odata/src/queryableprop.ts +++ b/packages/odata/src/queryableprop.ts @@ -1,138 +1,192 @@ -export interface QueryableInterface { +export interface QueryableInterface { readonly queryableInterface: unique symbol; + readonly typeProps: P; + readonly selectedProps: S; + readonly expandedProps: E; } export interface QueryableProp< PropType, - Expandable extends boolean, - DefaultSelected extends boolean, - Selected extends boolean, - Expanded extends boolean> { + Expandable extends boolean> { readonly symbol: unique symbol; readonly propType: PropType; readonly expandable: Expandable; - readonly defaultSelected: DefaultSelected; - readonly selected: Selected; - readonly expanded: Expanded; } export interface QueryableCompositeProp< PropType, ParentProp extends string, - ChildProp extends string, - DefaultSelected extends boolean, - Selected extends boolean, - Expanded extends boolean> { + ChildProp extends string> { readonly symbol: unique symbol; readonly propType: PropType; readonly parentProp: ParentProp; readonly childProp: ChildProp; - readonly defaultSelected: DefaultSelected; - readonly selected: Selected; - readonly expanded: Expanded; +} + +export interface QueryableODataProp< + PropType, + ParentProp extends string> { + readonly symbol: unique symbol; + readonly propType: PropType; + readonly parentProp: ParentProp; } export type QueryableSelectableKeys = T extends (infer A)[] ? QueryableSelectableKeysImpl : QueryableSelectableKeysImpl; -type QueryableSelectableKeysImpl = { - [U in keyof T]: - T[U] extends QueryableCompositeProp ? - never : - T[U] extends symbol ? - never : - U; -}[keyof T]; +type QueryableSelectableKeysImpl = + T extends QueryableInterface ? { + [U in keyof P]: + P[U] extends QueryableCompositeProp ? + ParentProp extends E ? + U : + never : + P[U] extends QueryableODataProp ? + never : + P[U] extends symbol ? + never : + U; + }[keyof P] : + string; export type QueryableSelect> = T extends (infer A)[] ? QueryableSelectImpl>[] : QueryableSelectImpl; -type QueryableSelectImpl> = T extends QueryableInterface ? { - [U in keyof T]: - U extends K ? - T[U] extends QueryableProp ? - QueryableProp : - T[U] extends QueryableCompositeProp ? - QueryableCompositeProp : - T[U] : - T[U]; -} : T; +type QueryableSelectImpl> = T extends QueryableInterface ? QueryableInterface : T; export type QueryableExpandableKeys = T extends (infer A)[] ? QueryableExpandableKeysImpl : QueryableExpandableKeysImpl; -type QueryableExpandableKeysImpl = { - [U in keyof T]: - T[U] extends QueryableProp ? - never: - T[U] extends QueryableCompositeProp ? - never : - T[U] extends symbol ? - never : - U; -}[keyof T]; +type QueryableExpandableKeysImpl = + T extends QueryableInterface ? { + [U in keyof P]: + P[U] extends QueryableProp ? + U : + never; + }[keyof P] : + string; export type QueryableExpand> = T extends (infer A)[] ? QueryableExpandImpl>[] : QueryableExpandImpl; -type QueryableExpandImpl> = T extends QueryableInterface ? { - [U in keyof T]: - U extends K ? - T[U] extends QueryableProp ? - QueryableProp : - T[U] : - T[U] extends QueryableCompositeProp ? - ParentProp extends K ? - QueryableCompositeProp : - T[U] : - T[U]; -} : T; +type QueryableExpandImpl> = T extends QueryableInterface ? QueryableInterface : T; export type QueryableSelectedProps = T extends (infer A)[] ? QueryableSelectedPropsImpl : QueryableSelectedPropsImpl; -type QueryableSelectedPropsImpl = { - [U in keyof T]: - U extends string ? - T[U] extends QueryableProp ? - U : - T[U] extends QueryableProp ? - U : - T[U] extends QueryableCompositeProp ? - never : - T[U] extends symbol ? - never : +type QueryableSelectedPropsImpl = + T extends QueryableInterface ? + { + [U in keyof P]: + P[U] extends QueryableProp ? + U extends E ? U : never : + P[U] extends QueryableProp ? + U extends S ? U : never : + P[U] extends QueryableODataProp ? + ParentProp extends QueryableSelectedODataParentPropsImpl ? + U : + never : + never; + + }[keyof P] : + string; + +type QueryableSelectedODataParentPropsImpl = + T extends QueryableInterface ? + { + [U in keyof P]: + P[U] extends QueryableProp ? + never : + P[U] extends QueryableProp ? + U extends S ? U : - never; -}[keyof T]; + U extends E ? + U : + never : + never; + }[keyof P] : + never; export type QueryableSelectedCompositeChildProps = T extends (infer A)[] ? QueryableSelectedCompositeChildPropsImpl : QueryableSelectedCompositeChildPropsImpl; -type QueryableSelectedCompositeChildPropsImpl = { - [U in keyof T]: - T[U] extends QueryableCompositeProp ? - ChildProp : - T[U] extends QueryableCompositeProp ? - ChildProp : - never; -}[keyof T]; +type QueryableSelectedCompositeChildPropsImpl = + T extends QueryableInterface ? + { + [U in keyof P]: + P[U] extends QueryableCompositeProp ? + ParentProp extends E ? + PropType extends QueryableODataProp ? + ChildProp : + U extends S ? + ChildProp : + ParentProp extends QueryableTouchedExpandedPropsImpl ? + never : + ChildProp : + never : + never; + }[keyof P] : + string; + +type QueryableTouchedExpandedPropsImpl = + T extends QueryableInterface ? + { + [U in keyof P]: + P[U] extends QueryableCompositeProp ? + ParentProp extends E ? + U extends S ? + ParentProp : + never : + never : + never; + }[keyof P] : + string; export type QueryableCompositePropType = T extends (infer A)[] ? QueryableCompositePropTypeImpl : QueryableCompositePropTypeImpl; -type QueryableCompositePropTypeImpl = { - [U in keyof T]: - T[U] extends QueryableCompositeProp ? - PropType : - never; -}[keyof T]; - -export type QueryableGet = - T extends QueryableInterface ? - (T extends (infer A)[] ? QueryableGetImpl[] : QueryableGetImpl) : +type QueryableCompositePropTypeImpl = + T extends QueryableInterface ? + { + [U in keyof P]: + P[U] extends QueryableCompositeProp ? + PropType extends QueryableODataProp ? + ODataPropType : + PropType : + never; + }[keyof P] : + never; + +export type QueryableGet = T extends (infer A)[] ? QueryableGetImpl[] : QueryableGetImpl; +type QueryableGetImpl = + T extends QueryableInterface ? + { + [U in (QueryableSelectedProps & keyof P)]: + P[U] extends QueryableProp ? + PropType : + P[U] extends QueryableProp ? + { + [V in QueryableSelectedCompositeChildProps]: QueryableCompositePropType; + } : + // tslint:disable-next-line:no-shadowed-variable + P[U] extends QueryableODataProp ? + PropType : + never + } : T; -type QueryableGetImpl = { - [U in QueryableSelectedProps]: - T[U] extends QueryableProp ? - PropType : - T[U] extends QueryableProp ? - PropType : - T[U] extends QueryableProp ? - { - [V in QueryableSelectedCompositeChildProps]: QueryableCompositePropType; - } & PropType : - T[U] -}; export type QueryableOrderableKeys = T extends (infer A)[] ? keyof A : keyof T; + + + + +type Test = QueryableInterface<{ + AllowAutomaticASPXPageIndexing: QueryableProp; + Id: QueryableProp; + ParentWeb: QueryableProp; + "ParentWeb@odata.navigationLinkUrl": QueryableODataProp; + "ParentWeb/Title": QueryableCompositeProp; + "ParentWeb/odata.editLink": QueryableCompositeProp; + "ParentWeb/odata.id": QueryableCompositeProp; + "ParentWeb/odata.type": QueryableCompositeProp<"SP.WebInfo", "ParentWeb", "odata.type">; + Title: QueryableProp; + "odata.editLink": QueryableODataProp; + "odata.id": QueryableODataProp; + "odata.metadata": QueryableODataProp; + "odata.type": QueryableODataProp<"SP.Web", any>; +}, "Id" | "Title" | "ParentWeb/Title", never>; + +type T0 = QueryableExpandableKeys; +type T1 = QueryableExpand; +type T2 = QueryableSelect; +type T3 = QueryableGet; diff --git a/packages/sp/src/webs.ts b/packages/sp/src/webs.ts index 2341499da..dd4b7795e 100644 --- a/packages/sp/src/webs.ts +++ b/packages/sp/src/webs.ts @@ -1,5 +1,5 @@ import { extend, TypedHash, jsS } from "@pnp/common"; -import { QueryableInterface, QueryableProp, QueryableCompositeProp } from "@pnp/odata"; +import { QueryableInterface, QueryableProp, QueryableCompositeProp, QueryableODataProp } from "@pnp/odata"; import { SharePointQueryableCollection, defaultPath } from "./sharepointqueryable"; import { SharePointQueryableShareableWeb } from "./sharepointqueryableshareable"; import { Folders, Folder } from "./folders"; @@ -28,7 +28,7 @@ import { ClientSidePage, ClientSidePageComponent } from "./clientsidepages"; * */ @defaultPath("webs") -export class Webs extends SharePointQueryableCollection { +export class Webs extends SharePointQueryableCollection { /** * Adds a new web to the collection @@ -85,7 +85,7 @@ export class WebInfos extends SharePointQueryableCollection {} * */ @defaultPath("_api/web") -export class Web extends SharePointQueryableShareableWeb { +export class Web extends SharePointQueryableShareableWeb { /** * Creates a new web instance from the given url by indexing the location of the /_api/ @@ -98,6 +98,14 @@ export class Web extends SharePointQueryableShareableWeb return new Web(extractWebUrl(url), path); } + /** + * Provides a strongly typed version of the query methods (select, expand, get) + * + */ + public stronglyTyped(): Web { + return this; + } + /** * Gets this web's subwebs * @@ -111,7 +119,7 @@ export class Web extends SharePointQueryableShareableWeb * */ public getParentWeb(): Promise<{ data: any; web: Web }> { - return this.expand("ParentWeb").select("ParentWeb/Id").get() + return this.stronglyTyped().expand("ParentWeb").select("ParentWeb/Id").get() .then(({ ParentWeb }) => new Site(this.toUrlAndQuery().split("/_api")[0]).openWebById(ParentWeb.Id)); } @@ -621,116 +629,194 @@ export interface WebEnsureUserResult { user: SiteUser; } -export interface WebQueryableInterface extends QueryableInterface { - Alerts: QueryableProp; - AllowAutomaticASPXPageIndexing: QueryableProp; - AllowCreateDeclarativeWorkflowForCurrentUser: QueryableProp; - AllowDesignerForCurrentUser: QueryableProp; - AllowMasterPageEditingForCurrentUser: QueryableProp; - AllowRevertFromTemplateForCurrentUser: QueryableProp; - AllowRssFeeds: QueryableProp; - AllowSaveDeclarativeWorkflowAsTemplateForCurrentUser: QueryableProp; - AllowSavePublishDeclarativeWorkflowForCurrentUser: QueryableProp; - AllProperties: QueryableProp; - AlternateCssUrl: QueryableProp; - AppInstanceId: QueryableProp; - AssociatedMemberGroup: QueryableProp; - AssociatedOwnerGroup: QueryableProp; - AssociatedVisitorGroup: QueryableProp; - Author: QueryableProp; - AvailableContentTypes: QueryableProp; - AvailableFields: QueryableProp; - Configuration: QueryableProp; - ContentTypes: QueryableProp; - Created: QueryableProp; - CurrentChangeToken: QueryableProp<{StringValue: string}, false, true, false, false>; - CurrentUser: QueryableProp<{}, true, false, false, false>; - "CurrentUser/Email": QueryableCompositeProp; - "CurrentUser/Id": QueryableCompositeProp; - "CurrentUser/IsEmailAuthenticationGuestUser": QueryableCompositeProp; - "CurrentUser/IsHiddenInUI": QueryableCompositeProp; - "CurrentUser/IsShareByEmailGuestUser": QueryableCompositeProp; - "CurrentUser/IsSiteAdmin": QueryableCompositeProp; - "CurrentUser/LoginName": QueryableCompositeProp; - "CurrentUser/PrincipalType": QueryableCompositeProp; - "CurrentUser/Title": QueryableCompositeProp; - "CurrentUser/UserId": QueryableCompositeProp<{ NameId: string, NameIdIssuer: string }, "CurrentUser", "UserId", true, false, false>; - CustomMasterUrl: QueryableProp; - Description: QueryableProp; - DescriptionResource: QueryableProp; - DesignerDownloadUrlForCurrentUser: QueryableProp; - DesignPackageId: QueryableProp; - DocumentLibraryCalloutOfficeWebAppPreviewersDisabled: QueryableProp; - EffectiveBasePermissions: QueryableProp<{High: string; Low: string}, false, false, false, false>; - EnableMinimalDownload: QueryableProp; - EventReceivers: QueryableProp; - ExcludeFromOfflineClient: QueryableProp; - Features: QueryableProp; - Fields: QueryableProp; - FirstUniqueAncestorSecurableObject: QueryableProp; - Folders: QueryableProp; - FooterEnabled: QueryableProp; - HasUniqueRoleAssignments: QueryableProp; - HeaderEmphasis: QueryableProp; - HeaderLayout: QueryableProp; - HorizontalQuickLaunch: QueryableProp; - Id: QueryableProp; - IsMultilingual: QueryableProp; - Language: QueryableProp; - LastItemModifiedDate: QueryableProp; - LastItemUserModifiedDate: QueryableProp; - Lists: QueryableProp; - ListTemplates: QueryableProp; - MasterUrl: QueryableProp; - MegaMenuEnabled: QueryableProp; - Navigation: QueryableProp; - NoCrawl: QueryableProp; - ObjectCacheEnabled: QueryableProp; - OverwriteTranslationsOnChange: QueryableProp; - ParentWeb: QueryableProp; - "ParentWeb/Configuration": QueryableCompositeProp; - "ParentWeb/Created": QueryableCompositeProp; - "ParentWeb/Description": QueryableCompositeProp; - "ParentWeb/Id": QueryableCompositeProp; - "ParentWeb/Language": QueryableCompositeProp; - "ParentWeb/LastItemModifiedDate": QueryableCompositeProp; - "ParentWeb/LastItemUserModifiedDate": QueryableCompositeProp; - "ParentWeb/ServerRelativeUrl": QueryableCompositeProp; - "ParentWeb/Title": QueryableCompositeProp; - "ParentWeb/WebTemplate": QueryableCompositeProp; - "ParentWeb/WebTemplateId": QueryableCompositeProp; - PushNotificationSubscribers: QueryableProp; - QuickLaunchEnabled: QueryableProp; - RecycleBin: QueryableProp; - RecycleBinEnabled: QueryableProp; - RegionalSettings: QueryableProp; - RequestAccessEmail: QueryableProp; - ResourcePath: QueryableProp<{DecodedUrl: string}, false, true, false, false>; - RoleAssignments: QueryableProp; - RoleDefinitions: QueryableProp; - RootFolder: QueryableProp; - SaveSiteAsTemplateEnabled: QueryableProp; - ServerRelativeUrl: QueryableProp; - ShowUrlStructureForCurrentUser: QueryableProp; - SiteGroups: QueryableProp; - SiteLogoDescription: QueryableProp; - SiteLogoUrl: QueryableProp; - SiteUserInfoList: QueryableProp; - SiteUsers: QueryableProp; - SupportedUILanguageIds: QueryableProp; - SyndicationEnabled: QueryableProp; - ThemedCssFolderUrl: QueryableProp; - ThemeInfo: QueryableProp; - Title: QueryableProp; - TitleResource: QueryableProp; - TreeViewEnabled: QueryableProp; - UIVersion: QueryableProp; - UIVersionConfigurationEnabled: QueryableProp; - Url: QueryableProp; - UserCustomActions: QueryableProp; - Webs: QueryableProp; - WebTemplate: QueryableProp; - WelcomePage: QueryableProp; - WorkflowAssociations: QueryableProp; - WorkflowTemplates: QueryableProp; -} +export type WebDefaultSelectedKeys = + "AllowRssFeeds" | + "AlternateCssUrl" | + "AppInstanceId" | + "Configuration" | + "Created" | + "CurrentChangeToken" | + "CurrentUser/Email" | + "CurrentUser/Id" | + "CurrentUser/IsEmailAuthenticationGuestUser" | + "CurrentUser/IsHiddenInUI" | + "CurrentUser/IsShareByEmailGuestUser" | + "CurrentUser/IsSiteAdmin" | + "CurrentUser/LoginName" | + "CurrentUser/PrincipalType" | + "CurrentUser/Title" | + "CurrentUser/UserId" | + "CurrentUser/odata.editLink" | + "CurrentUser/odata.id" | + "CurrentUser/odata.type" | + "CustomMasterUrl" | + "Description" | + "DesignPackageId" | + "DocumentLibraryCalloutOfficeWebAppPreviewersDisabled" | + "EnableMinimalDownload" | + "FooterEnabled" | + "HeaderEmphasis" | + "HeaderLayout" | + "HorizontalQuickLaunch" | + "Id" | + "IsMultilingual" | + "Language" | + "LastItemModifiedDate" | + "LastItemUserModifiedDate" | + "MasterUrl" | + "MegaMenuEnabled" | + "NoCrawl" | + "ObjectCacheEnabled" | + "OverwriteTranslationsOnChange" | + "QuickLaunchEnabled" | + "ParentWeb/Configuration" | + "ParentWeb/Created" | + "ParentWeb/Description" | + "ParentWeb/Id" | + "ParentWeb/Language" | + "ParentWeb/LastItemModifiedDate" | + "ParentWeb/LastItemUserModifiedDate" | + "ParentWeb/ServerRelativeUrl" | + "ParentWeb/Title" | + "ParentWeb/WebTemplate" | + "ParentWeb/WebTemplateId" | + "ParentWeb/odata.editLink" | + "ParentWeb/odata.id" | + "ParentWeb/odata.type" | + "RecycleBinEnabled" | + "ResourcePath" | + "ServerRelativeUrl" | + "SiteLogoUrl" | + "SyndicationEnabled" | + "Title" | + "TreeViewEnabled" | + "UIVersion" | + "UIVersionConfigurationEnabled" | + "Url" | + "WebTemplate" | + "WelcomePage"; +export type WebQueryableInterface = QueryableInterface<{ + Alerts: QueryableProp; + AllowAutomaticASPXPageIndexing: QueryableProp; + AllowCreateDeclarativeWorkflowForCurrentUser: QueryableProp; + AllowDesignerForCurrentUser: QueryableProp; + AllowMasterPageEditingForCurrentUser: QueryableProp; + AllowRevertFromTemplateForCurrentUser: QueryableProp; + AllowRssFeeds: QueryableProp; + AllowSaveDeclarativeWorkflowAsTemplateForCurrentUser: QueryableProp; + AllowSavePublishDeclarativeWorkflowForCurrentUser: QueryableProp; + AllProperties: QueryableProp; + AlternateCssUrl: QueryableProp; + AppInstanceId: QueryableProp; + AssociatedMemberGroup: QueryableProp; + AssociatedOwnerGroup: QueryableProp; + AssociatedVisitorGroup: QueryableProp; + Author: QueryableProp; + AvailableContentTypes: QueryableProp; + AvailableFields: QueryableProp; + Configuration: QueryableProp; + ContentTypes: QueryableProp; + Created: QueryableProp; + CurrentChangeToken: QueryableProp<{StringValue: string}, false>; + CurrentUser: QueryableProp; + "CurrentUser@odata.navigationLinkUrl": QueryableODataProp; + "CurrentUser/Email": QueryableCompositeProp; + "CurrentUser/Id": QueryableCompositeProp; + "CurrentUser/IsEmailAuthenticationGuestUser": QueryableCompositeProp; + "CurrentUser/IsHiddenInUI": QueryableCompositeProp; + "CurrentUser/IsShareByEmailGuestUser": QueryableCompositeProp; + "CurrentUser/IsSiteAdmin": QueryableCompositeProp; + "CurrentUser/LoginName": QueryableCompositeProp; + "CurrentUser/PrincipalType": QueryableCompositeProp; + "CurrentUser/Title": QueryableCompositeProp; + "CurrentUser/UserId": QueryableCompositeProp<{ NameId: string, NameIdIssuer: string }, "CurrentUser", "UserId">; + "CurrentUser/odata.editLink": QueryableCompositeProp, "CurrentUser", "odata.editLink">; + "CurrentUser/odata.id": QueryableCompositeProp, "CurrentUser", "odata.id">; + "CurrentUser/odata.type": QueryableCompositeProp, "CurrentUser", "odata.type">; + CustomMasterUrl: QueryableProp; + Description: QueryableProp; + DescriptionResource: QueryableProp; + DesignerDownloadUrlForCurrentUser: QueryableProp; + DesignPackageId: QueryableProp; + DocumentLibraryCalloutOfficeWebAppPreviewersDisabled: QueryableProp; + EffectiveBasePermissions: QueryableProp<{High: string; Low: string}, false>; + EnableMinimalDownload: QueryableProp; + EventReceivers: QueryableProp; + ExcludeFromOfflineClient: QueryableProp; + Features: QueryableProp; + Fields: QueryableProp; + FirstUniqueAncestorSecurableObject: QueryableProp; + Folders: QueryableProp; + FooterEnabled: QueryableProp; + HasUniqueRoleAssignments: QueryableProp; + HeaderEmphasis: QueryableProp; + HeaderLayout: QueryableProp; + HorizontalQuickLaunch: QueryableProp; + Id: QueryableProp; + IsMultilingual: QueryableProp; + Language: QueryableProp; + LastItemModifiedDate: QueryableProp; + LastItemUserModifiedDate: QueryableProp; + Lists: QueryableProp; + ListTemplates: QueryableProp; + MasterUrl: QueryableProp; + MegaMenuEnabled: QueryableProp; + Navigation: QueryableProp; + NoCrawl: QueryableProp; + ObjectCacheEnabled: QueryableProp; + OverwriteTranslationsOnChange: QueryableProp; + ParentWeb: QueryableProp; + "ParentWeb@odata.navigationLinkUrl": QueryableODataProp; + "ParentWeb/Configuration": QueryableCompositeProp; + "ParentWeb/Created": QueryableCompositeProp; + "ParentWeb/Description": QueryableCompositeProp; + "ParentWeb/Id": QueryableCompositeProp; + "ParentWeb/Language": QueryableCompositeProp; + "ParentWeb/LastItemModifiedDate": QueryableCompositeProp; + "ParentWeb/LastItemUserModifiedDate": QueryableCompositeProp; + "ParentWeb/ServerRelativeUrl": QueryableCompositeProp; + "ParentWeb/Title": QueryableCompositeProp; + "ParentWeb/WebTemplate": QueryableCompositeProp; + "ParentWeb/WebTemplateId": QueryableCompositeProp; + "ParentWeb/odata.editLink": QueryableCompositeProp, "ParentWeb", "odata.editLink">; + "ParentWeb/odata.id": QueryableCompositeProp, "ParentWeb", "odata.id">; + "ParentWeb/odata.type": QueryableCompositeProp, "ParentWeb", "odata.type">; + PushNotificationSubscribers: QueryableProp; + QuickLaunchEnabled: QueryableProp; + RecycleBin: QueryableProp; + RecycleBinEnabled: QueryableProp; + RegionalSettings: QueryableProp; + RequestAccessEmail: QueryableProp; + ResourcePath: QueryableProp<{DecodedUrl: string}, false>; + RoleAssignments: QueryableProp; + RoleDefinitions: QueryableProp; + RootFolder: QueryableProp; + SaveSiteAsTemplateEnabled: QueryableProp; + ServerRelativeUrl: QueryableProp; + ShowUrlStructureForCurrentUser: QueryableProp; + SiteGroups: QueryableProp; + SiteLogoDescription: QueryableProp; + SiteLogoUrl: QueryableProp; + SiteUserInfoList: QueryableProp; + SiteUsers: QueryableProp; + SupportedUILanguageIds: QueryableProp; + SyndicationEnabled: QueryableProp; + ThemedCssFolderUrl: QueryableProp; + ThemeInfo: QueryableProp; + Title: QueryableProp; + TitleResource: QueryableProp; + TreeViewEnabled: QueryableProp; + UIVersion: QueryableProp; + UIVersionConfigurationEnabled: QueryableProp; + Url: QueryableProp; + UserCustomActions: QueryableProp; + Webs: QueryableProp; + WebTemplate: QueryableProp; + WelcomePage: QueryableProp; + WorkflowAssociations: QueryableProp; + WorkflowTemplates: QueryableProp; + "odata.editLink": QueryableODataProp; + "odata.id": QueryableODataProp; + "odata.metadata": QueryableODataProp; + "odata.type": QueryableODataProp<"SP.Web", any>; +}, WebDefaultSelectedKeys, never>; From 9df490f16ee30b59968c36abd91f71039ea75a6d Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Sun, 21 Oct 2018 11:30:44 +0100 Subject: [PATCH 7/8] Wrote some tests --- packages/sp/tests/queryableinterface.test.ts | 270 +++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 packages/sp/tests/queryableinterface.test.ts diff --git a/packages/sp/tests/queryableinterface.test.ts b/packages/sp/tests/queryableinterface.test.ts new file mode 100644 index 000000000..aeb6d255d --- /dev/null +++ b/packages/sp/tests/queryableinterface.test.ts @@ -0,0 +1,270 @@ +import { extend, hOP } from "@pnp/common"; +import { PrincipalType } from "@pnp/sp"; +import { expect } from "chai"; +import { sp } from "../"; +import { testSettings } from "../../../test/main"; + +function checkProps(props: T) { + return props; +} +function extendWithODataProps(target: T, source: { "odata.editLink": E, "odata.id": I, "odata.metadata": M, "odata.type": J }) { + return extend(target, { + "odata.editLink": source["odata.editLink"], + "odata.id": source["odata.id"], + "odata.metadata": source["odata.metadata"], + "odata.type": source["odata.type"], + }); +} + +describe("WebQueryableInterface", () => { + + if (testSettings.enableWebTests) { + + describe("default get", () => { + it("should get the base object with default props", function () { + const expected = { + AllowRssFeeds: false, + AlternateCssUrl: "", + AppInstanceId: "", + Configuration: 0, + Created: "", + CurrentChangeToken: {StringValue: ""}, + CustomMasterUrl: "", + Description: "", + DesignPackageId: "", + DocumentLibraryCalloutOfficeWebAppPreviewersDisabled: false, + EnableMinimalDownload: false, + FooterEnabled: false, + HeaderEmphasis: 0, + HeaderLayout: 0, + HorizontalQuickLaunch: false, + Id: "", + IsMultilingual: false, + Language: 0, + LastItemModifiedDate: "", + LastItemUserModifiedDate: "", + MasterUrl: "", + MegaMenuEnabled: false, + NoCrawl: false, + ObjectCacheEnabled: false, + OverwriteTranslationsOnChange: false, + QuickLaunchEnabled: false, + RecycleBinEnabled: false, + ResourcePath: {DecodedUrl: ""}, + ServerRelativeUrl: "", + SiteLogoUrl: "", + SyndicationEnabled: false, + Title: "", + TreeViewEnabled: false, + UIVersion: 0, + UIVersionConfigurationEnabled: false, + Url: "", + WebTemplate: "", + WelcomePage: "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().get().then(web => { + // use 'typeof web' to check if the TS type has more properties than we selected at compile time + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("select default", () => { + it("should get a projection with selected props from default props", function () { + const expected = { + Title: "", + Url: "", + WelcomePage: "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().select("Title", "Url", "WelcomePage").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("select non-default", () => { + it("should get a projection with selected props from non-default props", function () { + const expected = { + AllowAutomaticASPXPageIndexing: false, + SaveSiteAsTemplateEnabled: false, + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().select("AllowAutomaticASPXPageIndexing", "SaveSiteAsTemplateEnabled").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("select non-expanded base", () => { + it("should select an odata navigation link", function () { + const expected = { + "CurrentUser@odata.navigationLinkUrl": "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().select("CurrentUser").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("expand with default get", () => { + it("should get the base object with default props and an expanded prop", function () { + const expected = { + AllowRssFeeds: false, + AlternateCssUrl: "", + AppInstanceId: "", + Configuration: 0, + Created: "", + CurrentChangeToken: {StringValue: ""}, + CurrentUser: { + Email: "", + Id: 0, + IsEmailAuthenticationGuestUser: false, + IsHiddenInUI: false, + IsShareByEmailGuestUser: false, + IsSiteAdmin: false, + LoginName: "", + PrincipalType: PrincipalType.User, + Title: "", + UserId: { + NameId: "", + NameIdIssuer: "", + }, + "odata.editLink": "", + "odata.id": "", + "odata.type": "SP.User" as "SP.User", + }, + "CurrentUser@odata.navigationLinkUrl": "", + CustomMasterUrl: "", + Description: "", + DesignPackageId: "", + DocumentLibraryCalloutOfficeWebAppPreviewersDisabled: false, + EnableMinimalDownload: false, + FooterEnabled: false, + HeaderEmphasis: 0, + HeaderLayout: 0, + HorizontalQuickLaunch: false, + Id: "", + IsMultilingual: false, + Language: 0, + LastItemModifiedDate: "", + LastItemUserModifiedDate: "", + MasterUrl: "", + MegaMenuEnabled: false, + NoCrawl: false, + ObjectCacheEnabled: false, + OverwriteTranslationsOnChange: false, + QuickLaunchEnabled: false, + RecycleBinEnabled: false, + ResourcePath: {DecodedUrl: ""}, + ServerRelativeUrl: "", + SiteLogoUrl: "", + SyndicationEnabled: false, + Title: "", + TreeViewEnabled: false, + UIVersion: 0, + UIVersionConfigurationEnabled: false, + Url: "", + WebTemplate: "", + WelcomePage: "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().expand("CurrentUser").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("expand with root prop select", () => { + it("should get the base object with selected prop and untouched expanded props", function () { + const expected = { + CurrentUser: { + Email: "", + Id: 0, + IsEmailAuthenticationGuestUser: false, + IsHiddenInUI: false, + IsShareByEmailGuestUser: false, + IsSiteAdmin: false, + LoginName: "", + PrincipalType: PrincipalType.User, + Title: "", + UserId: { + NameId: "", + NameIdIssuer: "", + }, + "odata.editLink": "", + "odata.id": "", + "odata.type": "SP.User" as "SP.User", + }, + "CurrentUser@odata.navigationLinkUrl": "", + Title: "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().expand("CurrentUser").select("Title").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("expand with expanded prop select", () => { + it("should get the base object with selected prop and untouched expanded props", function () { + const expected = { + CurrentUser: { + Id: 0, + "odata.editLink": "", + "odata.id": "", + "odata.type": "SP.User" as "SP.User", + }, + "CurrentUser@odata.navigationLinkUrl": "", + "odata.editLink": "", + "odata.id": "", + "odata.metadata": "", + "odata.type": "SP.Web" as "SP.Web", + }; + return expect(sp.web.stronglyTyped().expand("CurrentUser").select("CurrentUser/Id").get().then(web => { + checkProps(expected); + extend(expected, web, false, prop => hOP(expected, prop)); + return web; + })).to.eventually.deep.equal(expected); + }); + }); + + describe("backwards compatible", () => { + it("should be backwards compatible", function () { + return expect(sp.web.lists.select("Title").get()).to.eventually.fulfilled; + }); + }); + } +}); From 73a454f4728a480d7377d0f53aeedbecf342c0ee Mon Sep 17 00:00:00 2001 From: Pedro Pedrosa Date: Mon, 22 Oct 2018 18:33:40 +0100 Subject: [PATCH 8/8] Updated tests to test types both ways --- packages/sp/tests/queryableinterface.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/sp/tests/queryableinterface.test.ts b/packages/sp/tests/queryableinterface.test.ts index aeb6d255d..f82cced07 100644 --- a/packages/sp/tests/queryableinterface.test.ts +++ b/packages/sp/tests/queryableinterface.test.ts @@ -69,6 +69,7 @@ describe("WebQueryableInterface", () => { return expect(sp.web.stronglyTyped().get().then(web => { // use 'typeof web' to check if the TS type has more properties than we selected at compile time checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -88,6 +89,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().select("Title", "Url", "WelcomePage").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -106,6 +108,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().select("AllowAutomaticASPXPageIndexing", "SaveSiteAsTemplateEnabled").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -123,6 +126,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().select("CurrentUser").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -196,6 +200,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().expand("CurrentUser").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -232,6 +237,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().expand("CurrentUser").select("Title").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected); @@ -255,6 +261,7 @@ describe("WebQueryableInterface", () => { }; return expect(sp.web.stronglyTyped().expand("CurrentUser").select("CurrentUser/Id").get().then(web => { checkProps(expected); + checkProps(web); extend(expected, web, false, prop => hOP(expected, prop)); return web; })).to.eventually.deep.equal(expected);