diff --git a/data_mapper.ts b/data_mapper.ts index d569251..afec582 100644 --- a/data_mapper.ts +++ b/data_mapper.ts @@ -12,13 +12,16 @@ import { Operator, SimpleExpression, } from "./mod.ts"; -import { CountResponse } from "./types.ts"; import { AndConditions, + BulkDeleteResponse, + BulkGetResponse, + CountResponse, DataMapperExpressionCountArgs, DataMapperExpressionQueryArgs, DataMapperFindFirstExpressionQueryArgs, DataMapperIdQueryArgs, + DataMapperIdsQueryArgs, DataMapperInitArgs, DataMapperSaveArgs, Definition, @@ -92,6 +95,29 @@ export class DataMapper { }); } + async findAllByIds( + args: DataMapperIdsQueryArgs | string[], + ): Promise> { + let datastore = this.#defaultDatastore; + let ids: string[] | undefined = undefined; + if (Array.isArray(args)) { + ids = args; + } else { + ids = args.ids; + datastore = args.datastore ?? this.#defaultDatastore; + } + if (!datastore) { + throw new ConfigurationError(this.#datastoreMissingError); + } + const [client, logger] = [this.#client, this.#logger]; + return await func.findAllByIds({ + client, + datastore, + logger, + ids, + }); + } + async findFirstBy( args: | DataMapperFindFirstExpressionQueryArgs @@ -270,6 +296,28 @@ export class DataMapper { }); } + async deleteAllByIds( + args: DataMapperIdsQueryArgs | string[], + ): Promise { + let datastore = this.#defaultDatastore; + let ids: string[] | undefined = undefined; + if (Array.isArray(args)) { + ids = args; + } else { + ids = args.ids; + datastore = args.datastore ?? this.#defaultDatastore; + } + if (!datastore) { + throw new ConfigurationError(this.#datastoreMissingError); + } + return await func.deleteAllByIds({ + client: this.#client, + datastore, + ids, + logger: this.#logger, + }); + } + #datastoreMissingError = "`datastore` needs to be passed"; } diff --git a/functions.ts b/functions.ts index bddd1e4..829f0c7 100644 --- a/functions.ts +++ b/functions.ts @@ -6,12 +6,15 @@ import { } from "./dependencies/deno_slack_api_typed_method_types.ts"; import { DatastoreError } from "./errors.ts"; import { + BulkDeleteResponse, + BulkGetResponse, CountResponse, Definition, DeleteResponse, FindFirstRawExpressionQueryArgs, GetResponse, IdQueryArgs, + IdsQueryArgs, PutResponse, QueryResponse, RawExpressionQueryArgs, @@ -83,6 +86,33 @@ export async function findById({ return result as GetResponse; } +export async function findAllByIds({ + client, + datastore, + ids, + logger, +}: IdsQueryArgs): Promise> { + const _logger = logger ?? defaultLogger; + if (_logger.level === log.LogLevels.DEBUG) { + _logger.debug( + `${logName} Finding records (datastore: ${datastore}, IDs: ${ids})`, + ); + } + const result = await client.apps.datastore.bulkGet({ datastore, ids }); + if (_logger.level === log.LogLevels.DEBUG) { + const response = JSON.stringify(result); + _logger.debug( + `${logName} Found: (datastore: ${datastore}, response: ${response})`, + ); + } + if (result.error) { + const error = + `Failed to fetch rows (datastore: ${datastore}, error ${result.error})`; + throw new DatastoreError(error, result); + } + return result as BulkGetResponse; +} + export async function findFirstBy({ client, datastore, @@ -281,3 +311,30 @@ export async function deleteById({ } return result; } + +export async function deleteAllByIds({ + client, + datastore, + ids, + logger, +}: IdsQueryArgs): Promise { + const _logger = logger ?? defaultLogger; + if (_logger.level === log.LogLevels.DEBUG) { + _logger.debug( + `${logName} Deleting records (datastore: ${datastore}, IDs: ${ids})`, + ); + } + const result = await client.apps.datastore.bulkDelete({ datastore, ids }); + if (_logger.level === log.LogLevels.DEBUG) { + const jsonData = JSON.stringify(result); + _logger.debug( + `${logName} Deletion result: (datastore: ${datastore}, response: ${jsonData})`, + ); + } + if (result.error) { + const error = + `${logName} Failed to delete rows: (datastore: ${datastore}, error: ${result.error})`; + throw new DatastoreError(error, result); + } + return result; +} diff --git a/functions_test.ts b/functions_test.ts index 317d3be..24aee74 100644 --- a/functions_test.ts +++ b/functions_test.ts @@ -3,7 +3,8 @@ import { assertExists } from "./dependencies/testing_asserts.ts"; import { SlackAPI } from "./dependencies/deno_slack_api.ts"; import { save } from "./mod.ts"; import { DefineDatastore, Schema } from "./dependencies/deno_slack_sdk.ts"; -import { findAllBy } from "./functions.ts"; +import { countBy, findAllBy } from "./functions.ts"; +import { findAllByIds } from "./functions.ts"; mf.install(); @@ -45,6 +46,39 @@ mf.mock("POST@/api/apps.datastore.query", () => { }, ); }); +mf.mock("POST@/api/apps.datastore.bulkGet", () => { + return new Response( + JSON.stringify({ + "ok": true, + "datastore": "suveys", + "items": [ + { + "id": "123", + "title": "Off-site event ideas", + "questions": [ + "Can you share a fun idea for our off-site event in December?", + ], + "closed": false, + }, + ], + }), + { + status: 200, + }, + ); +}); +mf.mock("POST@/api/apps.datastore.count", () => { + return new Response( + JSON.stringify({ + "ok": true, + "datastore": "suveys", + "count": 123, + }), + { + status: 200, + }, + ); +}); export const Surveys = DefineDatastore( { @@ -106,3 +140,27 @@ Deno.test("Run a query", async () => { }); assertExists(result.items); }); + +Deno.test("Run a bulk get request", async () => { + const client = SlackAPI("valid-token"); + const result = await findAllByIds({ + client, + datastore: "suveys", + ids: ["1", "2", "3"], + }); + assertExists(result.items); +}); + +Deno.test("Run a count query", async () => { + const client = SlackAPI("valid-token"); + const result = await countBy({ + client, + datastore: "suveys", + expression: { + expression: "#id = :id", + attributes: { "#id": "id" }, + values: { ":id": "123" }, + }, + }); + assertExists(result.count); +}); diff --git a/test-app/functions/survey_demo.ts b/test-app/functions/survey_demo.ts index adc399c..eb82190 100644 --- a/test-app/functions/survey_demo.ts +++ b/test-app/functions/survey_demo.ts @@ -197,6 +197,11 @@ export default SlackFunction(def, async ({ client }) => { }); console.log(countResult); + const findByIdsResult = await mapper.findAllByIds({ + ids: ["1", "2", "3"], + }); + console.log(findByIdsResult); + const deletion1 = await mapper.deleteById({ id: "1" }); console.log(`deletion 1: ${JSON.stringify(deletion1, null, 2)}`); if (deletion1.error) { @@ -208,6 +213,11 @@ export default SlackFunction(def, async ({ client }) => { return { error: `Failed to delete a record - ${deletion2.error}` }; } + const deleteAllByIdsResult = await mapper.deleteAllByIds({ + ids: ["1", "2", "3"], + }); + console.log(deleteAllByIdsResult); + const alreadyInserted = (await mapper.findById({ id: "100" })).item; if (!alreadyInserted) { for (let i = 0; i < 100; i += 1) { diff --git a/types.ts b/types.ts index ad1fa8f..75df6c8 100644 --- a/types.ts +++ b/types.ts @@ -1,5 +1,7 @@ import * as log from "./dependencies/logger.ts"; import { + DatastoreBulkDeleteResponse, + DatastoreBulkGetResponse, DatastoreDeleteResponse, DatastoreGetResponse, DatastorePutResponse, @@ -172,6 +174,13 @@ export interface IdQueryArgs { logger?: log.Logger; } +export interface IdsQueryArgs { + client: SlackAPIClient; + datastore: string; + ids: string[]; + logger?: log.Logger; +} + export type RawExpressionCountArgs = { client: SlackAPIClient; datastore: string; @@ -201,6 +210,10 @@ export type GetResponse = & Omit, "item"> & { item: SavedAttributes }; +export type BulkGetResponse = + & Omit, "items"> + & { items: SavedAttributes }; + export type QueryResponse = & Omit, "items"> & { items: SavedAttributes[] }; @@ -209,6 +222,8 @@ export type CountResponse = DatastoreCountResponse; export type DeleteResponse = DatastoreDeleteResponse; +export type BulkDeleteResponse = DatastoreBulkDeleteResponse; + // ----------------------- // DataMapper's types // ----------------------- @@ -230,6 +245,11 @@ export interface DataMapperIdQueryArgs { datastore?: string; } +export interface DataMapperIdsQueryArgs { + ids: string[]; + datastore?: string; +} + export type PaginationArgs = CursorPaginationArgs & { autoPagination?: boolean; // default: true };