Skip to content

Commit

Permalink
Add(SCIMMY.Messages.{BulkRequest,SearchRequest}): support for passing…
Browse files Browse the repository at this point in the history
… arbitrary context to resource handlers
  • Loading branch information
sleelin committed Apr 21, 2024
1 parent 92c4fbb commit 30ad0b4
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 11 deletions.
13 changes: 7 additions & 6 deletions src/lib/messages/bulkrequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ export class BulkRequest {
/**
* Apply the operations specified by the supplied BulkRequest
* @param {typeof SCIMMY.Types.Resource[]} [resourceTypes] - resource type classes to be used while processing bulk operations, defaults to declared resources
* @param {*} [ctx] - any additional context information to pass to the ingress, egress, and degress handlers
* @returns {SCIMMY.Messages.BulkResponse} a new BulkResponse Message instance with results of the requested operations
*/
async apply(resourceTypes = Object.values(Resources.declared())) {
async apply(resourceTypes = Object.values(Resources.declared()), ctx) {
// Bail out if BulkRequest message has already been applied
if (this.#dispatched)
throw new TypeError("BulkRequest 'apply' method must not be called more than once");
Expand Down Expand Up @@ -198,7 +199,7 @@ export class BulkRequest {
const {id} = await new TargetResource().write(Object.entries(data)
// Remove any values that reference a bulkId
.filter(([,v]) => !JSON.stringify(v).includes("bulkId:"))
.reduce((res, [k, v]) => Object.assign(res, {[k]: v}), {}));
.reduce((res, [k, v]) => Object.assign(res, {[k]: v}), {}), ctx);

// Set the ID for future use and resolve pending references
Object.assign(data, {id})
Expand All @@ -210,7 +211,7 @@ export class BulkRequest {
data = Object.assign(JSON.parse(jsonData.replaceAll(`bulkId:${referenceId}`, await reference)), {id: data.id});
} catch (ex) {
// Referenced POST operation precondition failed, remove any created resource and bail out
if (bulkId && data.id) await new TargetResource(data.id).dispose();
if (bulkId && data.id) await new TargetResource(data.id).dispose(ctx);

// If we're following on from a prior failure, no need to explain why, otherwise, explain the failure
if (ex instanceof ErrorMessage && (!!errorLimit && errorCount >= errorLimit && index > lastErrorIndex)) return;
Expand All @@ -226,16 +227,16 @@ export class BulkRequest {
switch (method.toUpperCase()) {
case "POST":
case "PUT":
value = await resource.write(data);
value = await resource.write(data, ctx);
if (bulkId && !resource.id && value?.id) bulkIds.get(bulkId).resolve(value?.id);
break;

case "PATCH":
value = await resource.patch(data);
value = await resource.patch(data, ctx);
break;

case "DELETE":
await resource.dispose();
await resource.dispose(ctx);
break;
}

Expand Down
7 changes: 4 additions & 3 deletions src/lib/messages/searchrequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ export class SearchRequest {
/**
* Apply a search request operation, retrieving results from specified resource types
* @param {typeof SCIMMY.Types.Resource[]} [resourceTypes] - resource type classes to be used while processing the search request, defaults to declared resources
* @param {*} [ctx] - any additional context information to pass to the egress handler
* @returns {SCIMMY.Messages.ListResponse} a ListResponse message with results of the search request
*/
async apply(resourceTypes = Object.values(Resources.declared())) {
async apply(resourceTypes = Object.values(Resources.declared()), ctx) {
// Make sure all specified resource types extend the Resource type class so operations can be processed correctly
if (!Array.isArray(resourceTypes) || !resourceTypes.every(r => r.prototype instanceof Types.Resource))
throw new TypeError("Expected 'resourceTypes' parameter to be an array of Resource type classes in 'apply' method of SearchRequest");
Expand All @@ -122,12 +123,12 @@ export class SearchRequest {
// If only one resource type, just read from it
if (resourceTypes.length === 1) {
const [Resource] = resourceTypes;
return new Resource({...this, ...request}).read();
return new Resource({...this, ...request}).read(ctx);
}
// Otherwise, read from all resources and return collected results
else {
// Read from, and unwrap results for, supplied resource types
const results = await Promise.all(resourceTypes.map((Resource) => new Resource(request).read()))
const results = await Promise.all(resourceTypes.map((Resource) => new Resource(request).read(ctx)))
.then((r) => r.map((l) => l.Resources));

// Collect the results in a list response with specified constraints
Expand Down
4 changes: 2 additions & 2 deletions test/lib/messages/bulkrequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ describe("SCIMMY.Messages.BulkRequest", () => {

await (new BulkRequest({...template, Operations})).apply([TestStubbed]);

assert.ok(stub.called && stub.getCall(0)?.args?.length === 0,
assert.ok(stub.calledOnce,
"Instance method 'apply' did not dispose of newly created resource when circular bulkId operation failed");
});

Expand Down Expand Up @@ -384,7 +384,7 @@ describe("SCIMMY.Messages.BulkRequest", () => {

await (new BulkRequest({...template, Operations})).apply([TestStubbed]);

assert.ok(method !== "DELETE" ? stub.calledWithMatch(data) : stub.getCall(0)?.args?.length === 0,
assert.ok(method !== "DELETE" ? stub.calledWith(sinon.match.same(data)) : stub.calledOnce,
`Instance method 'apply' did not call resource instance '${fn}' method when 'method' attribute value was ${method}`);
});
}
Expand Down

0 comments on commit 30ad0b4

Please sign in to comment.