Return undefined for item reads (#343)

This commit is contained in:
Steve Faulkner 2019-06-11 16:35:14 -04:00 коммит произвёл GitHub
Родитель 5a50061a17
Коммит f0dcc48ed0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
32 изменённых файлов: 113 добавлений и 114 удалений

Просмотреть файл

@ -130,7 +130,7 @@ export class ChangeFeedIterator<T> {
return new ChangeFeedResponse(
response.result,
response.result ? response.result.length : 0,
response.statusCode,
response.code,
response.headers
);
}

Просмотреть файл

@ -294,10 +294,10 @@ export class ClientContext {
resultFn: (result: { [key: string]: any }) => any[]
): Response<any> {
if (isQuery) {
return { result: resultFn(res.result), headers: res.headers, statusCode: res.statusCode };
return { result: resultFn(res.result), headers: res.headers, code: res.code };
} else {
const newResult = resultFn(res.result).map((body: any) => body);
return { result: newResult, headers: res.headers, statusCode: res.statusCode };
return { result: newResult, headers: res.headers, code: res.code };
}
}

Просмотреть файл

@ -82,7 +82,7 @@ export class CosmosClient {
*/
public async getDatabaseAccount(options?: RequestOptions): Promise<ResourceResponse<DatabaseAccount>> {
const response = await this.clientContext.getDatabaseAccount(options);
return new ResourceResponse<DatabaseAccount>(response.result, response.headers, response.statusCode);
return new ResourceResponse<DatabaseAccount>(response.result, response.headers, response.code);
}
/**

Просмотреть файл

@ -42,7 +42,7 @@ export class Conflict {
resourceId: id,
options
});
return new ConflictResponse(response.result, response.headers, response.statusCode, this);
return new ConflictResponse(response.result, response.headers, response.code, this);
}
/**
@ -59,6 +59,6 @@ export class Conflict {
resourceId: id,
options
});
return new ConflictResponse(response.result, response.headers, response.statusCode, this);
return new ConflictResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -127,7 +127,7 @@ export class Container {
options
});
this.clientContext.partitionKeyDefinitionCache[this.url] = response.result.partitionKey;
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
return new ContainerResponse(response.result, response.headers, response.code, this);
}
/** Replace the container's definition */
@ -147,7 +147,7 @@ export class Container {
resourceId: id,
options
});
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
return new ContainerResponse(response.result, response.headers, response.code, this);
}
/** Delete the container */
@ -161,7 +161,7 @@ export class Container {
resourceId: id,
options
});
return new ContainerResponse(response.result, response.headers, response.statusCode, this);
return new ContainerResponse(response.result, response.headers, response.code, this);
}
/**

Просмотреть файл

@ -121,7 +121,7 @@ export class Containers {
options
});
const ref = new Container(this.database, response.result.id, this.clientContext);
return new ContainerResponse(response.result, response.headers, response.statusCode, ref);
return new ContainerResponse(response.result, response.headers, response.code, ref);
}
/**

Просмотреть файл

@ -85,7 +85,7 @@ export class Database {
resourceId: id,
options
});
return new DatabaseResponse(response.result, response.headers, response.statusCode, this);
return new DatabaseResponse(response.result, response.headers, response.code, this);
}
/** Delete the given Database. */
@ -98,6 +98,6 @@ export class Database {
resourceId: id,
options
});
return new DatabaseResponse(response.result, response.headers, response.statusCode, this);
return new DatabaseResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -111,7 +111,7 @@ export class Databases {
options
});
const ref = new Database(this.client, body.id, this.clientContext);
return new DatabaseResponse(response.result, response.headers, response.statusCode, ref);
return new DatabaseResponse(response.result, response.headers, response.code, ref);
}
/**

Просмотреть файл

@ -1,9 +1,17 @@
import { ClientContext } from "../../ClientContext";
import { createDocumentUri, getIdFromLink, getPathFromLink, isResourceValid, ResourceType } from "../../common";
import {
createDocumentUri,
getIdFromLink,
getPathFromLink,
isResourceValid,
ResourceType,
StatusCodes
} from "../../common";
import { PartitionKey } from "../../documents";
import { extractPartitionKey, undefinedPartitionKey } from "../../extractPartitionKey";
import { RequestOptions } from "../../request";
import { RequestOptions, Response } from "../../request";
import { Container } from "../Container";
import { Resource } from "../Resource";
import { ItemDefinition } from "./ItemDefinition";
import { ItemResponse } from "./ItemResponse";
@ -68,15 +76,23 @@ export class Item {
}
const path = getPathFromLink(this.url);
const id = getIdFromLink(this.url);
const response = await this.clientContext.read<T>({
path,
resourceType: ResourceType.item,
resourceId: id,
options,
partitionKey: this.partitionKey
});
let response: Response<T & Resource>;
try {
response = await this.clientContext.read<T>({
path,
resourceType: ResourceType.item,
resourceId: id,
options,
partitionKey: this.partitionKey
});
} catch (error) {
if (error.code !== StatusCodes.NotFound) {
throw error;
}
response = error;
}
return new ItemResponse(response.result, response.headers, response.statusCode, this);
return new ItemResponse(response.result, response.headers, response.code, response.substatus, this);
}
/**
@ -122,7 +138,7 @@ export class Item {
options,
partitionKey: this.partitionKey
});
return new ItemResponse(response.result, response.headers, response.statusCode, this);
return new ItemResponse(response.result, response.headers, response.code, response.substatus, this);
}
/**
@ -149,6 +165,6 @@ export class Item {
options,
partitionKey: this.partitionKey
});
return new ItemResponse(response.result, response.headers, response.statusCode, this);
return new ItemResponse(response.result, response.headers, response.code, response.substatus, this);
}
}

Просмотреть файл

@ -5,8 +5,8 @@ import { Item } from "./Item";
import { ItemDefinition } from "./ItemDefinition";
export class ItemResponse<T extends ItemDefinition> extends ResourceResponse<T & Resource> {
constructor(resource: T & Resource, headers: CosmosHeaders, statusCode: number, item: Item) {
super(resource, headers, statusCode);
constructor(resource: T & Resource, headers: CosmosHeaders, statusCode: number, subsstatusCode: number, item: Item) {
super(resource, headers, statusCode, subsstatusCode);
this.item = item;
}
/** Reference to the {@link Item} the response corresponds to. */

Просмотреть файл

@ -210,7 +210,7 @@ export class Items {
});
const ref = new Item(this.container, (response.result as any).id, partitionKey, this.clientContext);
return new ItemResponse(response.result, response.headers, response.statusCode, ref);
return new ItemResponse(response.result, response.headers, response.code, response.substatus, ref);
}
/**
@ -262,6 +262,6 @@ export class Items {
});
const ref = new Item(this.container, (response.result as any).id, partitionKey, this.clientContext);
return new ItemResponse(response.result, response.headers, response.statusCode, ref);
return new ItemResponse(response.result, response.headers, response.code, response.substatus, ref);
}
}

Просмотреть файл

@ -39,7 +39,7 @@ export class Offer {
resourceId: this.id,
options
});
return new OfferResponse(response.result, response.headers, response.statusCode, this);
return new OfferResponse(response.result, response.headers, response.code, this);
}
/**
@ -59,6 +59,6 @@ export class Offer {
resourceId: this.id,
options
});
return new OfferResponse(response.result, response.headers, response.statusCode, this);
return new OfferResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -39,7 +39,7 @@ export class Permission {
resourceId: id,
options
});
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
return new PermissionResponse(response.result, response.headers, response.code, this);
}
/**
@ -63,7 +63,7 @@ export class Permission {
resourceId: id,
options
});
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
return new PermissionResponse(response.result, response.headers, response.code, this);
}
/**
@ -80,6 +80,6 @@ export class Permission {
resourceId: id,
options
});
return new PermissionResponse(response.result, response.headers, response.statusCode, this);
return new PermissionResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -86,7 +86,7 @@ export class Permissions {
options
});
const ref = new Permission(this.user, response.result.id, this.clientContext);
return new PermissionResponse(response.result, response.headers, response.statusCode, ref);
return new PermissionResponse(response.result, response.headers, response.code, ref);
}
/**
@ -112,6 +112,6 @@ export class Permissions {
options
});
const ref = new Permission(this.user, response.result.id, this.clientContext);
return new PermissionResponse(response.result, response.headers, response.statusCode, ref);
return new PermissionResponse(response.result, response.headers, response.code, ref);
}
}

Просмотреть файл

@ -42,7 +42,7 @@ export class StoredProcedure {
resourceId: id,
options
});
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
return new StoredProcedureResponse(response.result, response.headers, response.code, this);
}
/**
@ -70,7 +70,7 @@ export class StoredProcedure {
resourceId: id,
options
});
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
return new StoredProcedureResponse(response.result, response.headers, response.code, this);
}
/**
@ -87,7 +87,7 @@ export class StoredProcedure {
resourceId: id,
options
});
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, this);
return new StoredProcedureResponse(response.result, response.headers, response.code, this);
}
/**
@ -106,6 +106,6 @@ export class StoredProcedure {
options?: RequestOptions
): Promise<ResourceResponse<T>> {
const response = await this.clientContext.execute<T>({ sprocLink: this.url, params, options, partitionKey });
return new ResourceResponse<T>(response.result, response.headers, response.statusCode);
return new ResourceResponse<T>(response.result, response.headers, response.code);
}
}

Просмотреть файл

@ -111,7 +111,7 @@ export class StoredProcedures {
options
});
const ref = new StoredProcedure(this.container, response.result.id, this.clientContext);
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, ref);
return new StoredProcedureResponse(response.result, response.headers, response.code, ref);
}
/**
@ -145,6 +145,6 @@ export class StoredProcedures {
options
});
const ref = new StoredProcedure(this.container, response.result.id, this.clientContext);
return new StoredProcedureResponse(response.result, response.headers, response.statusCode, ref);
return new StoredProcedureResponse(response.result, response.headers, response.code, ref);
}
}

Просмотреть файл

@ -43,7 +43,7 @@ export class Trigger {
resourceId: id,
options
});
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
return new TriggerResponse(response.result, response.headers, response.code, this);
}
/**
@ -71,7 +71,7 @@ export class Trigger {
resourceId: id,
options
});
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
return new TriggerResponse(response.result, response.headers, response.code, this);
}
/**
@ -88,6 +88,6 @@ export class Trigger {
resourceId: id,
options
});
return new TriggerResponse(response.result, response.headers, response.statusCode, this);
return new TriggerResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -91,7 +91,7 @@ export class Triggers {
options
});
const ref = new Trigger(this.container, response.result.id, this.clientContext);
return new TriggerResponse(response.result, response.headers, response.statusCode, ref);
return new TriggerResponse(response.result, response.headers, response.code, ref);
}
/**
@ -125,6 +125,6 @@ export class Triggers {
options
});
const ref = new Trigger(this.container, response.result.id, this.clientContext);
return new TriggerResponse(response.result, response.headers, response.statusCode, ref);
return new TriggerResponse(response.result, response.headers, response.code, ref);
}
}

Просмотреть файл

@ -62,7 +62,7 @@ export class User {
resourceId: id,
options
});
return new UserResponse(response.result, response.headers, response.statusCode, this);
return new UserResponse(response.result, response.headers, response.code, this);
}
/**
@ -86,7 +86,7 @@ export class User {
resourceId: id,
options
});
return new UserResponse(response.result, response.headers, response.statusCode, this);
return new UserResponse(response.result, response.headers, response.code, this);
}
/**
@ -103,6 +103,6 @@ export class User {
resourceId: id,
options
});
return new UserResponse(response.result, response.headers, response.statusCode, this);
return new UserResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -82,7 +82,7 @@ export class Users {
options
});
const ref = new User(this.database, response.result.id, this.clientContext);
return new UserResponse(response.result, response.headers, response.statusCode, ref);
return new UserResponse(response.result, response.headers, response.code, ref);
}
/**
@ -107,6 +107,6 @@ export class Users {
options
});
const ref = new User(this.database, response.result.id, this.clientContext);
return new UserResponse(response.result, response.headers, response.statusCode, ref);
return new UserResponse(response.result, response.headers, response.code, ref);
}
}

Просмотреть файл

@ -48,7 +48,7 @@ export class UserDefinedFunction {
resourceId: id,
options
});
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
return new UserDefinedFunctionResponse(response.result, response.headers, response.code, this);
}
/**
@ -79,7 +79,7 @@ export class UserDefinedFunction {
resourceId: id,
options
});
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
return new UserDefinedFunctionResponse(response.result, response.headers, response.code, this);
}
/**
@ -91,6 +91,6 @@ export class UserDefinedFunction {
const id = getIdFromLink(this.url);
const response = await this.clientContext.delete({ path, resourceType: ResourceType.udf, resourceId: id, options });
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, this);
return new UserDefinedFunctionResponse(response.result, response.headers, response.code, this);
}
}

Просмотреть файл

@ -93,7 +93,7 @@ export class UserDefinedFunctions {
options
});
const ref = new UserDefinedFunction(this.container, response.result.id, this.clientContext);
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, ref);
return new UserDefinedFunctionResponse(response.result, response.headers, response.code, ref);
}
/**
@ -128,6 +128,6 @@ export class UserDefinedFunctions {
options
});
const ref = new UserDefinedFunction(this.container, response.result.id, this.clientContext);
return new UserDefinedFunctionResponse(response.result, response.headers, response.statusCode, ref);
return new UserDefinedFunctionResponse(response.result, response.headers, response.code, ref);
}
}

Просмотреть файл

@ -1,3 +1,4 @@
export { StatusCodes } from "./common";
export { extractPartitionKey } from "./extractPartitionKey";
export { setAuthorizationTokenHeaderUsingMasterKey } from "./auth";
export {

Просмотреть файл

@ -70,6 +70,8 @@ async function httpRequest(requestContext: RequestContext) {
headers[key] = value;
});
const substatus = parseInt(headers[Constants.HttpHeaders.SubStatus], 10);
if (response.status >= 400) {
const errorResponse: ErrorResponse = new Error(result.message);
@ -82,7 +84,7 @@ async function httpRequest(requestContext: RequestContext) {
}
if (Constants.HttpHeaders.SubStatus in headers) {
errorResponse.substatus = parseInt(headers[Constants.HttpHeaders.SubStatus], 10);
errorResponse.substatus = substatus;
}
if (Constants.HttpHeaders.RetryAfterInMilliseconds in headers) {
@ -94,7 +96,8 @@ async function httpRequest(requestContext: RequestContext) {
return {
headers,
result,
statusCode: response.status
code: response.status,
substatus
};
}

Просмотреть файл

@ -1,12 +1,13 @@
import { Constants } from "../common";
import { CosmosHeaders } from "../queryExecutionContext/CosmosHeaders";
import { StatusCode } from "./StatusCodes";
import { StatusCode, SubStatusCode } from "./StatusCodes";
export class ResourceResponse<TResource> {
constructor(
public readonly resource: TResource,
public readonly headers: CosmosHeaders,
public readonly statusCode: StatusCode
public readonly statusCode: StatusCode,
public readonly substatus?: SubStatusCode
) {}
public get requestCharge(): number {
return this.headers[Constants.HttpHeaders.RequestCharge] as number;

Просмотреть файл

@ -3,5 +3,6 @@ import { CosmosHeaders } from "../index";
export interface Response<T> {
headers: CosmosHeaders;
result?: T;
statusCode?: number;
code?: number;
substatus?: number;
}

Просмотреть файл

@ -90,13 +90,9 @@ describe("Item CRUD", function() {
const { resource: res } = await container.item(replacedDocument.id, undefined).delete();
// read documents after deletion
try {
const { resource: document3 } = await container.item(replacedDocument.id, undefined).read();
assert.fail("must throw if document doesn't exist");
} catch (err) {
const notFoundErrorCode = 404;
assert.equal(err.code, notFoundErrorCode, "response should return error code 404");
}
const response = await container.item(replacedDocument.id, undefined).read();
assert.equal(response.statusCode, 404, "response should return error code 404");
assert.equal(response.resource, undefined);
};
it("Should do document CRUD operations successfully", async function() {

Просмотреть файл

@ -2,7 +2,7 @@ import assert from "assert";
import { CosmosClient, Constants, Container } from "../../dist-esm";
import { removeAllDatabases, getTestContainer } from "../common/TestHelpers";
import { endpoint, masterKey } from "../common/_testConfig";
import { ResourceType, HTTPMethod } from "../../dist-esm/common";
import { ResourceType, HTTPMethod, StatusCodes } from "../../dist-esm/common";
const legacyClient = new CosmosClient({
endpoint,
@ -84,12 +84,8 @@ describe("Non Partitioned Container", function() {
await container.item(item1.id, undefined).delete();
// read documents after deletion
try {
await container.item(item1.id, undefined).read();
assert.fail("must throw if document doesn't exist");
} catch (err) {
const notFoundErrorCode = 404;
assert.equal(err.code, notFoundErrorCode, "response should return error code 404");
}
const response = await container.item(item1.id, undefined).read();
assert.equal(response.statusCode, StatusCodes.NotFound);
assert.equal(response.resource, undefined);
});
});

Просмотреть файл

@ -8,7 +8,7 @@ describe("Plugin", function() {
it("should handle all requests", async function() {
const successResponse = {
headers: {},
statusCode: 200,
code: 200,
result: {
message: "yay"
}
@ -38,14 +38,14 @@ describe("Plugin", function() {
const response = await client.database("foo").read();
assert.equal(requestCount, FAILCOUNT + 1); // Get Database Account + FAILED GET Database + Get Database
assert.notEqual(response, undefined);
assert.equal(response.statusCode, successResponse.statusCode);
assert.equal(response.statusCode, successResponse.code);
assert.deepEqual(response.resource, successResponse.result);
});
it("should handle all operations", async function() {
const successResponse = {
headers: {},
statusCode: 200,
code: 200,
result: {
message: "yay"
}
@ -76,14 +76,14 @@ describe("Plugin", function() {
const response = await client.database("foo").read();
assert.equal(requestCount, 2); // Get Database Account + Get Database
assert.notEqual(response, undefined);
assert.equal(response.statusCode, successResponse.statusCode);
assert.equal(response.statusCode, successResponse.code);
assert.deepEqual(response.resource, successResponse.result);
});
it("should allow next to be called", async function() {
const successResponse = {
headers: {},
statusCode: 200,
code: 200,
result: {
message: "yay"
}
@ -122,7 +122,7 @@ describe("Plugin", function() {
assert.equal(responseCount, 2); // Get Database Account + Get Database
assert.equal(innerRequestCount, 2); // Get Database Account + Get Database
assert.notEqual(response, undefined);
assert.equal(response.statusCode, successResponse.statusCode);
assert.equal(response.statusCode, successResponse.code);
assert.deepEqual(response.resource, successResponse.result);
});
});

Просмотреть файл

@ -1,6 +1,7 @@
import assert from "assert";
import { Container, ContainerDefinition, Database } from "../../dist-esm/client";
import { getTestDatabase, removeAllDatabases } from "../common/TestHelpers";
import { StatusCodes } from "../../dist-esm";
async function sleep(time: number) {
return new Promise(resolve => {
@ -73,13 +74,9 @@ describe("Container TTL", function() {
});
async function checkItemGone(container: Container, createdItem: any) {
try {
await container.item(createdItem.id, undefined).read();
assert.fail("Must throw if the Item isn't there");
} catch (err) {
const badRequestErrorCode = 404;
assert.equal(err.code, badRequestErrorCode, "response should return error code " + badRequestErrorCode);
}
const response = await container.item(createdItem.id, undefined).read();
assert.equal(response.statusCode, StatusCodes.NotFound);
assert.equal(response.resource, undefined);
}
async function checkItemExists(container: Container, createdItem: any) {

Просмотреть файл

@ -34,10 +34,6 @@ describe("Change Feed Iterator", function() {
await container.items.create({ id: "item2", key: "1" });
});
after(async function() {
await container.delete();
});
it("should throw if used with no partition key or partition key range id", async function() {
const iterator = container.items.readChangeFeed({ startFromBeginning: true });

Просмотреть файл

@ -291,16 +291,12 @@ describe("Session Token", function() {
requestContext.headers[Constants.HttpHeaders.SessionToken] = increaseLSN(oldTokens);
});
const applySessionTokenStub = sinon.stub(clientContext as any, "applySessionToken").callsFake(callbackSpy as any);
try {
const resp = await container.item("1", "1").read();
assert.fail("readDocument must throw");
} catch (err) {
assert.equal(err.substatus, 1002, "Substatus should indicate the LSN didn't catchup.");
assert.equal(callbackSpy.callCount, 1);
assert.equal(trimSlashes(callbackSpy.lastCall.args[0].path), containerLink + "/docs/1");
} finally {
applySessionTokenStub.restore();
}
const resp = await container.item("1", "1").read();
assert.equal(resp.resource, undefined);
assert.equal(resp.substatus, 1002, "Substatus should indicate the LSN didn't catchup.");
assert.equal(callbackSpy.callCount, 1);
assert.equal(trimSlashes(callbackSpy.lastCall.args[0].path), containerLink + "/docs/1");
applySessionTokenStub.restore();
await container.item("1", "1").read();
});
@ -352,15 +348,11 @@ describe("Session Token", function() {
.delete();
const setSessionTokenSpy = sinon.spy(sessionContainer, "set");
try {
await createdContainer.item(createdDocument.id, "1").read();
assert.fail("Must throw");
} catch (err) {
assert.equal(err.code, 404, "expecting 404 (Not found)");
assert.equal(err.substatus, undefined, "expecting substatus code to be undefined");
assert.equal(setSessionTokenSpy.callCount, 1, "unexpected number of calls to sesSessionToken");
} finally {
setSessionTokenSpy.restore();
}
const resp = await createdContainer.item(createdDocument.id, "1").read();
assert.equal(resp.resource, undefined);
assert.equal(resp.statusCode, 404, "expecting 404 (Not found)");
assert.equal(resp.substatus, undefined, "expecting substatus code to be undefined");
assert.equal(setSessionTokenSpy.callCount, 1, "unexpected number of calls to sesSessionToken");
setSessionTokenSpy.restore();
});
});