From 19445ef25fb3ca14bc8ff2efa30ccf515d223083 Mon Sep 17 00:00:00 2001 From: "patrick.rodgers" Date: Mon, 2 Jan 2017 13:59:44 -0500 Subject: [PATCH] rewrite of the odata pipeline to move responsibility 100% to parsers for handling response, update to rreturn an object with etag for item update --- src/sharepoint/index.ts | 1 + src/sharepoint/items.ts | 33 +++++++++++++++++++++---- src/sharepoint/odata.ts | 22 ++++++++++++++++- src/sharepoint/queryable.ts | 48 ++++--------------------------------- 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/src/sharepoint/index.ts b/src/sharepoint/index.ts index 373cccb5..cf0ca24f 100644 --- a/src/sharepoint/index.ts +++ b/src/sharepoint/index.ts @@ -25,6 +25,7 @@ export { export { ItemAddResult, ItemUpdateResult, + ItemUpdateResultData, PagedItemCollection } from "./items"; diff --git a/src/sharepoint/items.ts b/src/sharepoint/items.ts index d5a15445..cdf55c25 100644 --- a/src/sharepoint/items.ts +++ b/src/sharepoint/items.ts @@ -188,7 +188,7 @@ export class Item extends QueryableSecurable { "IF-Match": eTag, "X-HTTP-Method": "MERGE", }, - }).then((data) => { + }, new ItemUpdatedParser()).then((data) => { removeDependency(); resolve({ data: data, @@ -257,7 +257,11 @@ export interface ItemAddResult { export interface ItemUpdateResult { item: Item; - data: any; + data: ItemUpdateResultData; +} + +export interface ItemUpdateResultData { + "odata.etag": string; } /** @@ -291,9 +295,28 @@ export class PagedItemCollection { class PagedItemCollectionParser extends ODataParserBase> { public parse(r: Response): Promise> { - return r.json().then(json => { - let nextUrl = json.hasOwnProperty("d") && json.d.hasOwnProperty("__next") ? json.d.__next : json["odata.nextLink"]; - return new PagedItemCollection(nextUrl, this.parseODataJSON(json)); + return new Promise>((resolve, reject) => { + + if (this.handleError(r, reject)) { + r.json().then(json => { + let nextUrl = json.hasOwnProperty("d") && json.d.hasOwnProperty("__next") ? json.d.__next : json["odata.nextLink"]; + resolve(new PagedItemCollection(nextUrl, this.parseODataJSON(json))); + }); + } + }); + } +} + +class ItemUpdatedParser extends ODataParserBase { + public parse(r: Response): Promise { + + return new Promise((resolve, reject) => { + + if (this.handleError(r, reject)) { + resolve({ + "odata.etag": r.headers.get("etag"), + }); + } }); } } diff --git a/src/sharepoint/odata.ts b/src/sharepoint/odata.ts index 26754363..2dc1419c 100644 --- a/src/sharepoint/odata.ts +++ b/src/sharepoint/odata.ts @@ -5,6 +5,7 @@ import { HttpClient } from "../net/httpclient"; import { RuntimeConfig } from "../configuration/pnplibconfig"; import { TypedHash } from "../collections/collections"; import { ODataIdException, BatchParseException } from "../utils/exceptions"; +import { ProcessHttpClientResponseException } from "../utils/exceptions"; export function extractOdataId(candidate: any): string { @@ -24,7 +25,26 @@ export interface ODataParser { export abstract class ODataParserBase implements ODataParser { public parse(r: Response): Promise { - return r.json().then(json => this.parseODataJSON(json)); + + return new Promise((resolve, reject) => { + + if (this.handleError(r, reject)) { + if ((r.headers.has("Content-Length") && parseFloat(r.headers.get("Content-Length")) === 0) || r.status === 204) { + resolve({}); + } else { + r.json().then(json => resolve(this.parseODataJSON(json))); + } + } + }); + } + + protected handleError(r: Response, reject: (reason?: any) => void): boolean { + if (!r.ok) { + r.json().then(json => { + reject(new ProcessHttpClientResponseException(r.status, r.statusText, json)); + }); + } + return r.ok; } protected parseODataJSON(json: any): U { diff --git a/src/sharepoint/queryable.ts b/src/sharepoint/queryable.ts index 2c3d428b..ecb6628e 100644 --- a/src/sharepoint/queryable.ts +++ b/src/sharepoint/queryable.ts @@ -4,7 +4,7 @@ import { FetchOptions, HttpClient } from "../net/httpclient"; import { ODataParser, ODataDefaultParser, ODataBatch } from "./odata"; import { ICachingOptions, CachingParserWrapper, CachingOptions } from "./caching"; import { RuntimeConfig } from "../configuration/pnplibconfig"; -import { ProcessHttpClientResponseException, AlreadyInBatchException } from "../utils/exceptions"; +import { AlreadyInBatchException } from "../utils/exceptions"; export interface QueryableConstructor { new (baseUrl: string | Queryable, path?: string): T; @@ -271,9 +271,7 @@ export class Queryable { // we are not part of a batch, so proceed as normal let client = new HttpClient(); - return client.get(this.toUrlAndQuery(), getOptions).then((response) => { - return this.processHttpClientResponse(response, parser); - }); + return client.get(this.toUrlAndQuery(), getOptions).then((response) => parser.parse(response)); } else { @@ -287,9 +285,7 @@ export class Queryable { // we are not part of a batch, so proceed as normal let client = new HttpClient(); - return client.post(this.toUrlAndQuery(), postOptions).then((response) => { - return this.processHttpClientResponse(response, parser); - }); + return client.post(this.toUrlAndQuery(), postOptions).then((response) => parser.parse(response)); } else { return this._batch.add(this.toUrlAndQuery(), "POST", postOptions, parser); @@ -302,9 +298,7 @@ export class Queryable { // we are not part of a batch, so proceed as normal let client = new HttpClient(); - return client.patch(this.toUrlAndQuery(), patchOptions).then((response) => { - return this.processHttpClientResponse(response, parser); - }); + return client.patch(this.toUrlAndQuery(), patchOptions).then((response) => parser.parse(response)); } else { return this._batch.add(this.toUrlAndQuery(), "PATCH", patchOptions, parser); @@ -317,44 +311,12 @@ export class Queryable { // we are not part of a batch, so proceed as normal let client = new HttpClient(); - return client.delete(this.toUrlAndQuery(), deleteOptions).then((response) => { - return this.processHttpClientResponse(response, parser); - }); + return client.delete(this.toUrlAndQuery(), deleteOptions).then((response) => parser.parse(response)); } else { return this._batch.add(this.toUrlAndQuery(), "DELETE", deleteOptions, parser); } } - - private processHttpClientResponse(response: Response, parser: ODataParser): Promise { - - return new Promise((resolve, reject) => { - - // 200 = OK (get, delete) - // 201 = Created (create) - // 204 = No Content (update) - if (response.ok) { - - if ((response.headers.has("Content-Length") && parseFloat(response.headers.get("Content-Length")) === 0) - || response.status === 204) { - - // in these cases the server has returned no content, so we create an empty object - // this was done because the fetch browser methods throw exceptions with no content - return resolve({}); - } - - // pipe our parsed content - return resolve(parser.parse(response)); - - } else { - - // and reject - response.json().then(json => { - return reject(new ProcessHttpClientResponseException(response.status, response.statusText, json)); - }); - } - }); - } } /**