diff --git a/apps/backend/src/modules/api/controllers/organizations.controller.ts b/apps/backend/src/modules/api/controllers/organizations.controller.ts index c4279bbe..7f52c8d6 100644 --- a/apps/backend/src/modules/api/controllers/organizations.controller.ts +++ b/apps/backend/src/modules/api/controllers/organizations.controller.ts @@ -9,7 +9,12 @@ import { import { ConfigService } from '~/modules/config'; import { OrganizationsService } from '~/modules/organizations'; -import { rejectUnsafeSdkErrors, sdkSchemaValidator, serializeSdkResponseTE } from '../helpers'; +import { + rejectUnsafeCreateSdkErrors, + rejectUnsafeSdkErrors, + sdkSchemaValidator, + serializeSdkResponseTE, +} from '../helpers'; import { AuthorizedController } from './shared/authorized.controller'; @injectable() @@ -37,6 +42,7 @@ export class OrganizationsController extends AuthorizedController { async context => pipe( context.req.valid('json'), organizationsService.asUser(context.var.jwt).create, + rejectUnsafeCreateSdkErrors, rejectUnsafeSdkErrors, serializeSdkResponseTE>(context), ), diff --git a/apps/backend/src/modules/api/helpers/index.ts b/apps/backend/src/modules/api/helpers/index.ts index abd8da1f..8e6823c8 100644 --- a/apps/backend/src/modules/api/helpers/index.ts +++ b/apps/backend/src/modules/api/helpers/index.ts @@ -1,3 +1,4 @@ +export * from './reject-unsafe-create-sdk-errors'; export * from './reject-unsafe-sdk-errors'; export * from './respond-with-tagged-error'; export * from './sdk-hono-schema-validator'; diff --git a/apps/backend/src/modules/api/helpers/reject-unsafe-create-sdk-errors.ts b/apps/backend/src/modules/api/helpers/reject-unsafe-create-sdk-errors.ts new file mode 100644 index 00000000..41d64e4a --- /dev/null +++ b/apps/backend/src/modules/api/helpers/reject-unsafe-create-sdk-errors.ts @@ -0,0 +1,45 @@ +import { taskEither as TE } from 'fp-ts'; +import { pipe } from 'fp-ts/function'; + +import type { TaggedError } from '@llm/commons'; + +import { + isSdkTaggedError, + SdkRecordAlreadyExistsError, +} from '@llm/sdk'; +import { LoggerService } from '~/modules/logger'; + +export function rejectUnsafeCreateSdkErrors>(task: TE.TaskEither) { + const logger = LoggerService.of('rejectUnsafeSdkErrors'); + + return pipe( + task, + TE.mapLeft((error) => { + if (isSdkTaggedError(error)) { + return error; + } + + const mappedError = (() => { + switch (error.tag) { + case 'DatabaseRecordAlreadyExists': + return new SdkRecordAlreadyExistsError({}); + + default: + return error; + } + })(); + + if (mappedError !== error) { + const { stack, ...context } = error; + + logger.error(`Mapped creator SDK error - ${error.tag}!`, context); + + if (stack) { + console.error(stack); + } + } + + return mappedError; + }), + ); +} diff --git a/apps/backend/src/modules/api/helpers/reject-unsafe-sdk-errors.ts b/apps/backend/src/modules/api/helpers/reject-unsafe-sdk-errors.ts index e3fe5fbb..34ce44e2 100644 --- a/apps/backend/src/modules/api/helpers/reject-unsafe-sdk-errors.ts +++ b/apps/backend/src/modules/api/helpers/reject-unsafe-sdk-errors.ts @@ -5,7 +5,6 @@ import type { TaggedError } from '@llm/commons'; import { isSdkTaggedError, - SdkRecordAlreadyExistsError, SdkServerError, } from '@llm/sdk'; import { LoggerService } from '~/modules/logger'; @@ -28,15 +27,9 @@ export function rejectUnsafeSdkErrors>(tas console.error(stack); } - switch (error.tag) { - case 'DatabaseRecordAlreadyExists': - return new SdkRecordAlreadyExistsError({}); - - default: - return new SdkServerError({ - message: 'Internal server error!', - }); - } + return new SdkServerError({ + message: 'Internal server error!', + }); }), ); } diff --git a/apps/backend/src/modules/elasticsearch/mappings/archived.mapping.ts b/apps/backend/src/modules/elasticsearch/mappings/archived.mapping.ts new file mode 100644 index 00000000..846ee2f6 --- /dev/null +++ b/apps/backend/src/modules/elasticsearch/mappings/archived.mapping.ts @@ -0,0 +1,5 @@ +export function createArchivedRecordMappings() { + return { + archived: { type: 'boolean' }, + }; +} diff --git a/apps/backend/src/modules/elasticsearch/mappings/index.ts b/apps/backend/src/modules/elasticsearch/mappings/index.ts index 1b14a024..64318821 100644 --- a/apps/backend/src/modules/elasticsearch/mappings/index.ts +++ b/apps/backend/src/modules/elasticsearch/mappings/index.ts @@ -1,3 +1,4 @@ +export * from './archived.mapping'; export * from './autocomplete.mapping'; export * from './dated-record.mapping'; export * from './id-name-object.mapping'; diff --git a/apps/backend/src/modules/elasticsearch/repo/elasticsearch.repo.ts b/apps/backend/src/modules/elasticsearch/repo/elasticsearch.repo.ts index 5ec2c9cd..d6f5e461 100644 --- a/apps/backend/src/modules/elasticsearch/repo/elasticsearch.repo.ts +++ b/apps/backend/src/modules/elasticsearch/repo/elasticsearch.repo.ts @@ -156,14 +156,21 @@ export class ElasticsearchRepo { indexDocument = ( indexName: string, { _id, ...doc }: D, - { waitForRecordAvailability }: EsIndexWaitAttributes = {}, + { waitForRecordAvailability = true }: EsIndexWaitAttributes = {}, ) => pipe( - TaggedError.tryUnsafeTask(EsIndexingError, async () => this.client.index({ - id: _id.toString(), - index: indexName, - body: doc, - })), + TaggedError.tryUnsafeTask(EsIndexingError, async () => { + this.logger.info('Trying to index document...', { _id, doc, indexName }); + + const result = await this.client.index({ + id: _id.toString(), + index: indexName, + body: doc, + }); + + this.logger.info('Document indexed!', { _id, indexName }); + return result; + }), TE.chainW(() => ( waitForRecordAvailability @@ -212,6 +219,8 @@ export class ElasticsearchRepo { TE.tryCatch( () => waitFor( async () => { + this.logger.info('Waiting for document availability...', { indexName, id }); + const result = await this.client.exists({ index: indexName, id, @@ -226,6 +235,8 @@ export class ElasticsearchRepo { ); } + this.logger.info('Looks like document is available!', { indexName, id }); + return true; }, { diff --git a/apps/backend/src/modules/organizations/elasticsearch/organizations-es-index.repo.ts b/apps/backend/src/modules/organizations/elasticsearch/organizations-es-index.repo.ts index 471301b3..1c7fdc73 100644 --- a/apps/backend/src/modules/organizations/elasticsearch/organizations-es-index.repo.ts +++ b/apps/backend/src/modules/organizations/elasticsearch/organizations-es-index.repo.ts @@ -5,6 +5,7 @@ import { inject, injectable } from 'tsyringe'; import { tryOrThrowTE } from '@llm/commons'; import { + createArchivedRecordMappings, createAutocompleteFieldAnalyzeSettings, createBaseAutocompleteFieldMappings, createBaseDatedRecordMappings, @@ -25,6 +26,7 @@ const OrganizationsAbstractEsIndexRepo = createElasticsearchIndexRepo({ properties: { ...createBaseDatedRecordMappings(), ...createBaseAutocompleteFieldMappings(), + ...createArchivedRecordMappings(), max_number_of_users: { type: 'integer' }, }, }, diff --git a/apps/backend/src/modules/users/elasticsearch/users-es-index.repo.ts b/apps/backend/src/modules/users/elasticsearch/users-es-index.repo.ts index 994db5e3..d9f99316 100644 --- a/apps/backend/src/modules/users/elasticsearch/users-es-index.repo.ts +++ b/apps/backend/src/modules/users/elasticsearch/users-es-index.repo.ts @@ -5,6 +5,7 @@ import { inject, injectable } from 'tsyringe'; import { tryOrThrowTE } from '@llm/commons'; import { + createArchivedRecordMappings, createAutocompleteFieldAnalyzeSettings, createBaseAutocompleteFieldMappings, createBaseDatedRecordMappings, @@ -25,11 +26,11 @@ const UsersAbstractEsIndexRepo = createElasticsearchIndexRepo({ dynamic: false, properties: { ...createBaseDatedRecordMappings(), + ...createArchivedRecordMappings(), ...createBaseAutocompleteFieldMappings('email'), role: { type: 'keyword' }, email: { type: 'text' }, active: { type: 'boolean' }, - archived: { type: 'boolean' }, archive_protection: { type: 'boolean' }, auth: { properties: { diff --git a/packages/sdk/src/modules/dashboard/organizations/organizations.sdk.ts b/packages/sdk/src/modules/dashboard/organizations/organizations.sdk.ts index b6c61b84..87b94e98 100644 --- a/packages/sdk/src/modules/dashboard/organizations/organizations.sdk.ts +++ b/packages/sdk/src/modules/dashboard/organizations/organizations.sdk.ts @@ -1,5 +1,10 @@ import { AbstractNestedSdkWithAuth } from '~/modules/abstract-nested-sdk-with-auth'; -import { getPayload, performApiRequest, postPayload } from '~/shared'; +import { + getPayload, + performApiRequest, + postPayload, + type SdkRecordAlreadyExistsError, +} from '~/shared'; import type { SdkCreateOrganizationInputT, @@ -19,7 +24,7 @@ export class OrganizationsSdk extends AbstractNestedSdkWithAuth { }); create = (data: SdkCreateOrganizationInputT) => - performApiRequest({ + performApiRequest({ url: this.endpoint('/create'), options: postPayload(data), }); diff --git a/packages/sdk/src/shared/dto/sdk-search-filters.dto.ts b/packages/sdk/src/shared/dto/sdk-search-filters.dto.ts index 8c01fb66..b1111201 100644 --- a/packages/sdk/src/shared/dto/sdk-search-filters.dto.ts +++ b/packages/sdk/src/shared/dto/sdk-search-filters.dto.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; import { SdkTableRowIdV } from './sdk-table-row-id.dto'; export const SdkArchivedFiltersInputV = z.object({ - archived: z.coerce.boolean().optional(), + archived: z.coerce.boolean().optional().default(false), }); export const SdkIdsFiltersInputV = z.object({