2021-12-07 15:30:53 +01:00
|
|
|
import o from "ospec"
|
2022-01-07 15:58:30 +01:00
|
|
|
import {Contact, ContactTypeRef, createContact} from "../../../src/api/entities/tutanota/Contact"
|
2021-12-07 15:30:53 +01:00
|
|
|
import {BadRequestError, InternalServerError, PayloadTooLargeError} from "../../../src/api/common/error/RestError"
|
|
|
|
|
import {assertThrows} from "@tutao/tutanota-test-utils"
|
|
|
|
|
import {SetupMultipleError} from "../../../src/api/common/error/SetupMultipleError"
|
|
|
|
|
import {downcast, TypeRef} from "@tutao/tutanota-utils"
|
2022-01-07 15:58:30 +01:00
|
|
|
import {HttpMethod, MediaType, resolveTypeReference} from "../../../src/api/common/EntityFunctions"
|
|
|
|
|
import {createCustomer, Customer, CustomerTypeRef} from "../../../src/api/entities/sys/Customer"
|
2021-12-15 16:07:07 +01:00
|
|
|
import {EntityRestClient, typeRefToPath} from "../../../src/api/worker/rest/EntityRestClient"
|
2021-12-07 15:30:53 +01:00
|
|
|
import {RestClient} from "../../../src/api/worker/rest/RestClient"
|
|
|
|
|
import type {CryptoFacade} from "../../../src/api/worker/crypto/CryptoFacade"
|
|
|
|
|
import type {TypeModel} from "../../../src/api/common/EntityTypes"
|
|
|
|
|
import {createInternalRecipientKeyData} from "../../../src/api/entities/tutanota/InternalRecipientKeyData"
|
|
|
|
|
import {create} from "../../../src/api/common/utils/EntityUtils"
|
|
|
|
|
import {InstanceMapper} from "../../../src/api/worker/crypto/InstanceMapper"
|
|
|
|
|
import {CalendarEventTypeRef} from "../../../src/api/entities/tutanota/CalendarEvent"
|
2022-01-07 15:58:30 +01:00
|
|
|
import {ProgressListener} from "../../../src/api/common/utils/ProgressMonitor";
|
2021-12-07 15:30:53 +01:00
|
|
|
|
2021-12-15 16:07:07 +01:00
|
|
|
const accessToken = "My cool access token"
|
2022-01-07 15:58:30 +01:00
|
|
|
const authHeader = {
|
|
|
|
|
accessToken: accessToken,
|
|
|
|
|
}
|
2021-12-07 15:30:53 +01:00
|
|
|
|
2022-01-07 15:58:30 +01:00
|
|
|
function createEntityRestClientWithMocks(
|
2022-01-13 13:24:37 +01:00
|
|
|
requestMock: (...args: Array<any>) => any,
|
2022-01-07 15:58:30 +01:00
|
|
|
): {
|
|
|
|
|
entityRestClient: EntityRestClient
|
|
|
|
|
cryptoFacadeMock: CryptoFacade
|
|
|
|
|
instanceMapperMock: InstanceMapper
|
|
|
|
|
requestSpy: any
|
2021-12-15 16:07:07 +01:00
|
|
|
} {
|
|
|
|
|
const cryptoFacadeMock: CryptoFacade = {
|
2022-01-07 15:58:30 +01:00
|
|
|
// @ts-ignore
|
|
|
|
|
applyMigrations: o.spy(
|
2022-01-13 13:24:37 +01:00
|
|
|
async <T>(typeRef: TypeRef<T>, data: any): Promise<T> =>
|
|
|
|
|
resolveTypeReference(typeRef).then(model => create(model, typeRef)),
|
2022-01-07 15:58:30 +01:00
|
|
|
),
|
2021-12-15 16:07:07 +01:00
|
|
|
applyMigrationsForInstance: o.spy(async <T>(decryptedInstance: T): Promise<T> => decryptedInstance),
|
2022-01-07 15:58:30 +01:00
|
|
|
setNewOwnerEncSessionKey: o.spy((model: TypeModel, entity: Record<string, any>) => []),
|
|
|
|
|
resolveServiceSessionKey: o.spy(async (typeModel: TypeModel, instance: Record<string, any>) => []),
|
|
|
|
|
encryptBucketKeyForInternalRecipient: o.spy(
|
2022-01-13 13:24:37 +01:00
|
|
|
async (bucketKey: Aes128Key, recipientMailAddress: string, notFoundRecipients: Array<string>) =>
|
|
|
|
|
createInternalRecipientKeyData(),
|
2022-01-07 15:58:30 +01:00
|
|
|
),
|
|
|
|
|
resolveSessionKey: o.spy(async (typeModel: TypeModel, instance: Record<string, any>) => []),
|
2021-12-07 15:30:53 +01:00
|
|
|
}
|
2021-12-15 16:07:07 +01:00
|
|
|
const instanceMapperMock: InstanceMapper = downcast({
|
|
|
|
|
encryptAndMapToLiteral: o.spy(async (model, instance, sk) => {
|
|
|
|
|
return {
|
2022-01-07 15:58:30 +01:00
|
|
|
dummyMessage: "encrypted",
|
2021-12-15 16:07:07 +01:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
decryptAndMapToInstance: o.spy(async (model, instance, sk) => {
|
|
|
|
|
return {
|
2022-01-07 15:58:30 +01:00
|
|
|
dummyMessage: "decrypted",
|
2021-12-15 16:07:07 +01:00
|
|
|
}
|
2022-01-07 15:58:30 +01:00
|
|
|
}),
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
2021-12-07 15:30:53 +01:00
|
|
|
const requestSpy = o.spy(requestMock)
|
|
|
|
|
const entityRestClient = new EntityRestClient(
|
2022-01-13 13:24:37 +01:00
|
|
|
() => authHeader, // Entity rest client doesn't allow requests without authorization
|
|
|
|
|
downcast<RestClient>({
|
|
|
|
|
request: async (...args) => requestSpy(...args),
|
|
|
|
|
}),
|
|
|
|
|
() => cryptoFacadeMock,
|
|
|
|
|
instanceMapperMock,
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
return {
|
2021-12-15 16:07:07 +01:00
|
|
|
instanceMapperMock,
|
|
|
|
|
cryptoFacadeMock,
|
2021-12-07 15:30:53 +01:00
|
|
|
entityRestClient,
|
2022-01-07 15:58:30 +01:00
|
|
|
requestSpy,
|
2021-12-07 15:30:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createArrayOf<T>(count: number, factory: (index: number) => T): Array<T> {
|
2022-01-07 15:58:30 +01:00
|
|
|
return Array(count)
|
2022-01-13 13:24:37 +01:00
|
|
|
// @ts-ignore
|
|
|
|
|
.fill()
|
|
|
|
|
.map((_, idx) => factory(idx))
|
2021-12-07 15:30:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const range = (start, count) => createArrayOf(count, idx => String(idx + start))
|
|
|
|
|
|
|
|
|
|
function contacts(count) {
|
2022-01-07 15:58:30 +01:00
|
|
|
const contactFactory = idx =>
|
2022-01-13 13:24:37 +01:00
|
|
|
createContact({
|
|
|
|
|
firstName: `Contact${idx}`,
|
|
|
|
|
})
|
2022-01-07 15:58:30 +01:00
|
|
|
|
2021-12-07 15:30:53 +01:00
|
|
|
return createArrayOf(count, contactFactory)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o.spec("EntityRestClient", async function () {
|
2021-12-15 16:07:07 +01:00
|
|
|
o.spec("Load", function () {
|
|
|
|
|
o("loading a list element", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {
|
|
|
|
|
entityRestClient,
|
|
|
|
|
requestSpy,
|
|
|
|
|
instanceMapperMock,
|
|
|
|
|
cryptoFacadeMock,
|
|
|
|
|
} = createEntityRestClientWithMocks(() => JSON.stringify("The element that was returned from the server"))
|
2021-12-15 16:07:07 +01:00
|
|
|
const calendarListId = "calendarListId"
|
|
|
|
|
const id1 = "id1"
|
|
|
|
|
const result = await entityRestClient.load(CalendarEventTypeRef, [calendarListId, id1])
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[0]).equals(`${typeRefToPath(CalendarEventTypeRef)}/${calendarListId}/${id1}`)(
|
2022-01-13 13:24:37 +01:00
|
|
|
"path is correct",
|
2022-01-07 15:58:30 +01:00
|
|
|
)
|
2021-12-15 16:07:07 +01:00
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
// @ts-ignore
|
2021-12-15 16:07:07 +01:00
|
|
|
o(instanceMapperMock.decryptAndMapToInstance.callCount).equals(1)
|
2022-01-07 15:58:30 +01:00
|
|
|
// @ts-ignore
|
2021-12-15 16:07:07 +01:00
|
|
|
o(cryptoFacadeMock.applyMigrationsForInstance.callCount).equals(1)
|
|
|
|
|
})
|
|
|
|
|
o("loading an element ", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify("The element that was returned from the server"),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
2021-12-15 16:07:07 +01:00
|
|
|
const id1 = "id1"
|
|
|
|
|
const result = await entityRestClient.load(CustomerTypeRef, id1)
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.callCount).equals(1)
|
2021-12-15 16:07:07 +01:00
|
|
|
o(requestSpy.args[0]).equals(`${typeRefToPath(CustomerTypeRef)}/${id1}`)("path is correct")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-13 13:24:37 +01:00
|
|
|
o(result).deepEquals(downcast({dummyMessage: "decrypted"}))("decrypts and returns from the client")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
2022-01-07 15:58:30 +01:00
|
|
|
o(
|
2022-01-13 13:24:37 +01:00
|
|
|
"query parameters and additional headers + access token and version are always passed to the rest client",
|
|
|
|
|
async function () {
|
|
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
|
|
|
|
JSON.stringify("The element that was returned from the server"),
|
|
|
|
|
)
|
|
|
|
|
const id1 = "id1"
|
|
|
|
|
const queryParameters = {
|
|
|
|
|
foo: "bar",
|
|
|
|
|
}
|
|
|
|
|
const headers = {
|
|
|
|
|
baz: "quux",
|
|
|
|
|
}
|
|
|
|
|
const result = await entityRestClient.load(CustomerTypeRef, id1, queryParameters, headers)
|
|
|
|
|
const {version} = await resolveTypeReference(CustomerTypeRef)
|
|
|
|
|
o(requestSpy.args[2]).deepEquals(queryParameters)("query parameters are passed")
|
|
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken: accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
baz: "quux",
|
|
|
|
|
})("headers are passed")
|
|
|
|
|
},
|
2022-01-07 15:58:30 +01:00
|
|
|
)
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
o.spec("Load Range", function () {
|
|
|
|
|
o("Loads a range of entities in a single request", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {
|
|
|
|
|
entityRestClient,
|
|
|
|
|
requestSpy,
|
|
|
|
|
instanceMapperMock,
|
|
|
|
|
cryptoFacadeMock,
|
|
|
|
|
} = createEntityRestClientWithMocks(() => JSON.stringify(["e1", "e2", "e3"]))
|
2021-12-15 16:07:07 +01:00
|
|
|
const startId = "42"
|
|
|
|
|
const count = 5
|
|
|
|
|
await entityRestClient.loadRange(CalendarEventTypeRef, "listId", startId, count, false)
|
|
|
|
|
const {version} = await resolveTypeReference(CalendarEventTypeRef)
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.GET)("Method is GET")
|
|
|
|
|
o(requestSpy.args[2]).deepEquals({
|
|
|
|
|
start: `${startId}`,
|
|
|
|
|
count: `${count}`,
|
2022-01-07 15:58:30 +01:00
|
|
|
reverse: "false",
|
2021-12-15 16:07:07 +01:00
|
|
|
})("Range is passed in as query params")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
|
|
|
|
})
|
2021-12-07 15:30:53 +01:00
|
|
|
o.spec("Load multiple", function () {
|
|
|
|
|
o("Less than 100 entities requested should result in a single rest request", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {
|
|
|
|
|
entityRestClient,
|
|
|
|
|
requestSpy,
|
|
|
|
|
instanceMapperMock,
|
|
|
|
|
cryptoFacadeMock,
|
|
|
|
|
} = createEntityRestClientWithMocks(() => JSON.stringify(["e1", "e2", "e3"]))
|
2021-12-07 15:30:53 +01:00
|
|
|
const ids = range(0, 5)
|
|
|
|
|
const result = await entityRestClient.loadMultiple(CustomerTypeRef, null, ids)
|
2021-12-15 16:07:07 +01:00
|
|
|
const {version} = await resolveTypeReference(CustomerTypeRef)
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[2]).deepEquals({
|
|
|
|
|
ids: ids.join(","),
|
|
|
|
|
})("Requested IDs are passed in as query params")
|
|
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
|
|
|
|
// @ts-ignore
|
2021-12-15 16:07:07 +01:00
|
|
|
o(instanceMapperMock.decryptAndMapToInstance.callCount).equals(3)
|
2022-01-07 15:58:30 +01:00
|
|
|
// @ts-ignore
|
2021-12-15 16:07:07 +01:00
|
|
|
o(cryptoFacadeMock.applyMigrationsForInstance.callCount).equals(3)
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("Exactly 100 entities requested should result in a single rest request", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(["The elements that were returned from the server"]),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
const ids = range(0, 100)
|
|
|
|
|
const result = await entityRestClient.loadMultiple(CustomerTypeRef, null, ids)
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[2]).deepEquals({
|
|
|
|
|
ids: ids.join(","),
|
|
|
|
|
})("Requested IDs are passed in as query params")
|
2022-01-13 13:24:37 +01:00
|
|
|
o(result).deepEquals([downcast({dummyMessage: "decrypted"})])("Returns what was returned by the rest client")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("More than 100 entities requested results in 2 rest requests", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => JSON.stringify(["entities"]))
|
2021-12-07 15:30:53 +01:00
|
|
|
const ids = range(0, 101)
|
|
|
|
|
const result = await entityRestClient.loadMultiple(CustomerTypeRef, null, ids)
|
|
|
|
|
o(requestSpy.callCount).equals(2)
|
|
|
|
|
o(requestSpy.calls[0].args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[0].args[2]).deepEquals({
|
|
|
|
|
ids: ids.slice(0, 100).join(","),
|
|
|
|
|
})("The first 100 ids are requested")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[1].args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[1].args[2]).deepEquals({
|
|
|
|
|
ids: ids.slice(100, 101).join(","),
|
|
|
|
|
})("The remaining 1 id is requested")
|
2022-01-13 13:24:37 +01:00
|
|
|
o(result).deepEquals([downcast({dummyMessage: "decrypted"}), downcast({dummyMessage: "decrypted"})])("Returns what was returned by the rest client")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("More than 200 entities requested results in 3 rest requests", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => JSON.stringify(["entities"]))
|
2021-12-07 15:30:53 +01:00
|
|
|
const ids = range(0, 211)
|
|
|
|
|
const result = await entityRestClient.loadMultiple(CustomerTypeRef, null, ids)
|
|
|
|
|
o(requestSpy.callCount).equals(3)
|
|
|
|
|
o(requestSpy.calls[0].args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[0].args[2]).deepEquals({
|
|
|
|
|
ids: ids.slice(0, 100).join(","),
|
|
|
|
|
})("The first 100 ids are requested")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[1].args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[1].args[2]).deepEquals({
|
|
|
|
|
ids: ids.slice(100, 200).join(","),
|
|
|
|
|
})("The next 100 ids are requested")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[2].args[1]).equals(HttpMethod.GET)("Method is GET")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[2].args[2]).deepEquals({
|
|
|
|
|
ids: ids.slice(200, 211).join(","),
|
|
|
|
|
})("The remaining 11 ids are requested")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(result).deepEquals([
|
2022-01-13 13:24:37 +01:00
|
|
|
downcast({
|
2022-01-07 15:58:30 +01:00
|
|
|
dummyMessage: "decrypted",
|
2022-01-13 13:24:37 +01:00
|
|
|
}),
|
|
|
|
|
downcast({
|
2022-01-07 15:58:30 +01:00
|
|
|
dummyMessage: "decrypted",
|
2022-01-13 13:24:37 +01:00
|
|
|
}),
|
|
|
|
|
downcast({
|
2022-01-07 15:58:30 +01:00
|
|
|
dummyMessage: "decrypted",
|
2022-01-13 13:24:37 +01:00
|
|
|
}),
|
2021-12-07 15:30:53 +01:00
|
|
|
])("Returns what was returned by the rest client")
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
o.spec("Setup", async function () {
|
|
|
|
|
o("Setup list entity", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
generatedId: "id",
|
|
|
|
|
}),
|
2021-12-15 16:07:07 +01:00
|
|
|
)
|
|
|
|
|
const newContact = createContact()
|
|
|
|
|
const result = await entityRestClient.setup("listId", newContact)
|
|
|
|
|
const {version} = await resolveTypeReference(ContactTypeRef)
|
2022-01-07 15:58:30 +01:00
|
|
|
o(result).equals("id")
|
2021-12-15 16:07:07 +01:00
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
|
|
|
|
o(requestSpy.args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Contact were sent")
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
|
|
|
|
o("Setup list entity throws when no listid is passed", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
generatedId: "id",
|
|
|
|
|
}),
|
2021-12-15 16:07:07 +01:00
|
|
|
)
|
|
|
|
|
const newContact = createContact()
|
|
|
|
|
const result = await assertThrows(Error, async () => await entityRestClient.setup(null, newContact))
|
|
|
|
|
o(result.message).equals("List id must be defined for LETs")
|
|
|
|
|
})
|
|
|
|
|
o("Setup entity", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
generatedId: "id",
|
|
|
|
|
}),
|
2021-12-15 16:07:07 +01:00
|
|
|
)
|
|
|
|
|
const newCustomer = createCustomer()
|
|
|
|
|
const result = await entityRestClient.setup(null, newCustomer)
|
|
|
|
|
const {version} = await resolveTypeReference(CustomerTypeRef)
|
2022-01-07 15:58:30 +01:00
|
|
|
o(result).equals("id")
|
2021-12-15 16:07:07 +01:00
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
|
|
|
|
o(requestSpy.args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Contact were sent")
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
|
|
|
|
o("Setup entity throws when listid is passed", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
generatedId: "id",
|
|
|
|
|
}),
|
2021-12-15 16:07:07 +01:00
|
|
|
)
|
|
|
|
|
const newCustomer = createCustomer()
|
|
|
|
|
const result = await assertThrows(Error, async () => await entityRestClient.setup("listId", newCustomer))
|
|
|
|
|
o(result.message).equals("List id must not be defined for ETs")
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-07 15:30:53 +01:00
|
|
|
o.spec("Setup multiple", async function () {
|
|
|
|
|
o("Less than 100 entities created should result in a single rest request", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify([
|
|
|
|
|
{
|
|
|
|
|
generatedId: "someReturnedId",
|
|
|
|
|
},
|
|
|
|
|
]),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
const newContacts = contacts(1)
|
|
|
|
|
const result = await entityRestClient.setupMultiple("listId", newContacts)
|
2021-12-15 16:07:07 +01:00
|
|
|
const {version} = await resolveTypeReference(ContactTypeRef)
|
2021-12-07 15:30:53 +01:00
|
|
|
o(result).deepEquals(["someReturnedId"])
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
|
|
|
|
o(requestSpy.args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
})),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("All contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("Exactly 100 entities created should result in a single rest request", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify([
|
|
|
|
|
{
|
|
|
|
|
generatedId: "someReturnedId",
|
|
|
|
|
},
|
|
|
|
|
]),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
const newContacts = contacts(100)
|
|
|
|
|
const result = await entityRestClient.setupMultiple("listId", newContacts)
|
|
|
|
|
o(result).deepEquals(["someReturnedId"])
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
})),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("All contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("More than 100 entities created should result in 2 rest requests", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify([
|
|
|
|
|
{
|
|
|
|
|
generatedId: "someReturnedId",
|
|
|
|
|
},
|
|
|
|
|
]),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
const newContacts = contacts(101)
|
|
|
|
|
const result = await entityRestClient.setupMultiple("listId", newContacts)
|
|
|
|
|
o(result).deepEquals(["someReturnedId", "someReturnedId"])
|
|
|
|
|
o(requestSpy.callCount).equals(2)
|
|
|
|
|
o(requestSpy.calls[0].args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[0].args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts
|
|
|
|
|
.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}))
|
|
|
|
|
.slice(0, 100),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("First 100 contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[1].args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[1].args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts
|
|
|
|
|
.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}))
|
|
|
|
|
.slice(100, 101),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Remaining contact was sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
|
|
|
|
o("More than 200 entities created should result in 3 rest requests", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() =>
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify([
|
|
|
|
|
{
|
|
|
|
|
generatedId: "someReturnedId",
|
|
|
|
|
},
|
|
|
|
|
]),
|
2021-12-07 15:30:53 +01:00
|
|
|
)
|
|
|
|
|
const newContacts = contacts(211)
|
|
|
|
|
const result = await entityRestClient.setupMultiple("listId", newContacts)
|
|
|
|
|
o(result).deepEquals(["someReturnedId", "someReturnedId", "someReturnedId"])
|
|
|
|
|
o(requestSpy.callCount).equals(3)
|
|
|
|
|
o(requestSpy.calls[0].args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[0].args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts
|
|
|
|
|
.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}))
|
|
|
|
|
.slice(0, 100),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("First 100 contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[1].args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[1].args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts
|
|
|
|
|
.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}))
|
|
|
|
|
.slice(100, 200),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Next 100 contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
o(requestSpy.calls[2].args[1]).equals(HttpMethod.POST)("The method is POST")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.calls[2].args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify(
|
|
|
|
|
newContacts
|
|
|
|
|
.map(() => ({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}))
|
|
|
|
|
.slice(200, 211),
|
|
|
|
|
),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Remaining 11 contacts were sent")
|
2021-12-07 15:30:53 +01:00
|
|
|
})
|
2022-01-07 15:58:30 +01:00
|
|
|
o(
|
2022-01-13 13:24:37 +01:00
|
|
|
"A single request is made and an error occurs, all entites should be returned as failedInstances",
|
|
|
|
|
async function () {
|
|
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => {
|
|
|
|
|
throw new BadRequestError("canny do et")
|
|
|
|
|
})
|
|
|
|
|
const newContacts = contacts(100)
|
|
|
|
|
const result = await assertThrows(SetupMultipleError, () =>
|
|
|
|
|
entityRestClient.setupMultiple("listId", newContacts),
|
|
|
|
|
)
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(result.failedInstances.length).equals(newContacts.length)
|
|
|
|
|
o(result.errors.length).equals(1)
|
|
|
|
|
o(result.errors[0] instanceof BadRequestError).equals(true)
|
|
|
|
|
o(result.failedInstances).deepEquals(newContacts)
|
|
|
|
|
},
|
2022-01-07 15:58:30 +01:00
|
|
|
)
|
|
|
|
|
o(
|
2022-01-13 13:24:37 +01:00
|
|
|
"Post multiple: An error is encountered for part of the request, only failed entities are returned in the result",
|
|
|
|
|
async function () {
|
|
|
|
|
let requestCounter = 0
|
|
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => {
|
|
|
|
|
requestCounter += 1
|
2022-01-07 15:58:30 +01:00
|
|
|
|
2022-01-13 13:24:37 +01:00
|
|
|
if (requestCounter % 2 === 0) {
|
|
|
|
|
// Second and Fourth requests are success
|
|
|
|
|
return JSON.stringify(range(0, 100))
|
|
|
|
|
} else {
|
|
|
|
|
// First and Third requests are failure
|
|
|
|
|
throw new BadRequestError("It was a bad request")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const newContacts = contacts(400)
|
|
|
|
|
const result = await assertThrows(SetupMultipleError, () =>
|
|
|
|
|
entityRestClient.setupMultiple("listId", newContacts),
|
|
|
|
|
)
|
|
|
|
|
o(requestSpy.callCount).equals(4)
|
|
|
|
|
o(result.failedInstances).deepEquals(newContacts.slice(0, 100).concat(newContacts.slice(200, 300)))
|
|
|
|
|
o(result.errors.length).equals(2)
|
|
|
|
|
o(result.errors.every(e => e instanceof BadRequestError)).equals(true)
|
|
|
|
|
},
|
2022-01-07 15:58:30 +01:00
|
|
|
)
|
2021-12-07 15:30:53 +01:00
|
|
|
o("Post multiple: When a PayloadTooLarge error occurs individual instances are posted", async function () {
|
|
|
|
|
const listId = "listId"
|
|
|
|
|
const idArray = ["0", null, "2"] // GET fails for id 1
|
2022-01-07 15:58:30 +01:00
|
|
|
|
|
|
|
|
let instances: Contact[] = []
|
|
|
|
|
|
2021-12-07 15:30:53 +01:00
|
|
|
for (let i = 0; i < idArray.length; i++) {
|
|
|
|
|
instances.push(createContact())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let step = 0
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(function (
|
2022-01-13 13:24:37 +01:00
|
|
|
path: string,
|
|
|
|
|
method: HttpMethod,
|
|
|
|
|
queryParams: Dict,
|
|
|
|
|
headers: Dict,
|
|
|
|
|
body: string | null | undefined,
|
|
|
|
|
responseType: MediaType | null | undefined,
|
|
|
|
|
progressListener: ProgressListener | null | undefined,
|
|
|
|
|
baseUrl?: string,
|
2022-01-07 15:58:30 +01:00
|
|
|
) {
|
|
|
|
|
//post multiple - body is an array
|
|
|
|
|
if (body && body.startsWith("[")) {
|
|
|
|
|
throw new PayloadTooLargeError("test") //post single
|
|
|
|
|
} else if (step === 1) {
|
|
|
|
|
step += 1
|
|
|
|
|
throw new InternalServerError("might happen")
|
|
|
|
|
} else {
|
|
|
|
|
return JSON.stringify(idArray[step++])
|
2021-12-07 15:30:53 +01:00
|
|
|
}
|
2022-01-07 15:58:30 +01:00
|
|
|
})
|
2021-12-07 15:30:53 +01:00
|
|
|
const result = await assertThrows(SetupMultipleError, async () => {
|
|
|
|
|
return await entityRestClient.setupMultiple(listId, instances)
|
|
|
|
|
})
|
|
|
|
|
o(requestSpy.callCount).equals(4) //one post multiple and three individual posts
|
2022-01-07 15:58:30 +01:00
|
|
|
|
|
|
|
|
o(result.failedInstances.length).equals(1) //one individual post results in an error
|
|
|
|
|
|
2021-12-07 15:30:53 +01:00
|
|
|
o(result.errors.length).equals(1)
|
|
|
|
|
o(result.errors[0] instanceof InternalServerError).equals(true)
|
|
|
|
|
o(result.failedInstances).deepEquals([instances[1]])
|
|
|
|
|
})
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
o.spec("Update", function () {
|
|
|
|
|
o("Update entity", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => {
|
|
|
|
|
})
|
|
|
|
|
const newCustomer = createCustomer({
|
|
|
|
|
_id: "id",
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
await entityRestClient.update(newCustomer)
|
|
|
|
|
const {version} = await resolveTypeReference(CustomerTypeRef)
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.PUT)("The method is PUT")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
|
|
|
|
o(requestSpy.args[4]).deepEquals(
|
2022-01-13 13:24:37 +01:00
|
|
|
JSON.stringify({
|
|
|
|
|
dummyMessage: "encrypted",
|
|
|
|
|
}),
|
2022-01-07 15:58:30 +01:00
|
|
|
)("Contact were sent")
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
|
|
|
|
o("Update entity throws if entity does not have an id", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => {
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
const newCustomer = createCustomer()
|
|
|
|
|
const result = await assertThrows(Error, async () => await entityRestClient.update(newCustomer))
|
|
|
|
|
o(result.message).equals("Id must be defined")
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
o.spec("Delete", function () {
|
|
|
|
|
o("Delete entity", async function () {
|
2022-01-07 15:58:30 +01:00
|
|
|
const {entityRestClient, requestSpy} = createEntityRestClientWithMocks(() => {
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
const id = "id"
|
2022-01-07 15:58:30 +01:00
|
|
|
const newCustomer = createCustomer({
|
|
|
|
|
_id: id,
|
|
|
|
|
})
|
2021-12-15 16:07:07 +01:00
|
|
|
await entityRestClient.erase(newCustomer)
|
|
|
|
|
const {version} = await resolveTypeReference(CustomerTypeRef)
|
|
|
|
|
o(requestSpy.callCount).equals(1)
|
|
|
|
|
o(requestSpy.args[0]).equals(`${typeRefToPath(CustomerTypeRef)}/${id}`)("path is correct")
|
|
|
|
|
o(requestSpy.args[1]).equals(HttpMethod.DELETE)("The method is DELETE")
|
2022-01-07 15:58:30 +01:00
|
|
|
o(requestSpy.args[3]).deepEquals({
|
|
|
|
|
accessToken,
|
|
|
|
|
v: version,
|
|
|
|
|
})("access token and version are passed")
|
2021-12-15 16:07:07 +01:00
|
|
|
})
|
|
|
|
|
})
|
2022-01-07 15:58:30 +01:00
|
|
|
})
|