diff --git a/src/modules/measure/application/usecases/measureState.ts b/src/modules/measure/application/usecases/measureState.ts index cacd39e82..2d2d5c23e 100644 --- a/src/modules/measure/application/usecases/measureState.ts +++ b/src/modules/measure/application/usecases/measureState.ts @@ -1,21 +1,22 @@ import { createResource, createRoot } from 'solid-js' import { createMeasureCrud } from '~/modules/measure/application/usecases/measureCrud' -import { initializeMeasureRealtime } from '~/modules/measure/infrastructure/supabase/realtime' +import { createMeasureRealtimeService } from '~/modules/measure/infrastructure/supabase/realtime' + +const measureRealtimeService = createMeasureRealtimeService() export function createMeasureState(deps: { getCurrentUserIdOrGuestId: () => string fetchUserBodyMeasures?: ReturnType< typeof createMeasureCrud >['fetchUserBodyMeasures'] - initializeMeasureRealtime?: (deps: { - refetchBodyMeasures: () => void - }) => void + initializeMeasureRealtime?: typeof measureRealtimeService.initializeMeasureRealtime }) { const localFetch = deps.fetchUserBodyMeasures ?? createMeasureCrud().fetchUserBodyMeasures const localInitializeRealtime = - deps.initializeMeasureRealtime ?? initializeMeasureRealtime + deps.initializeMeasureRealtime ?? + measureRealtimeService.initializeMeasureRealtime return createRoot(() => { const [bodyMeasures, { refetch: refetchBodyMeasures }] = createResource( diff --git a/src/modules/measure/infrastructure/measureRepository.ts b/src/modules/measure/infrastructure/measureRepository.ts index adf5df72b..56ffcedec 100644 --- a/src/modules/measure/infrastructure/measureRepository.ts +++ b/src/modules/measure/infrastructure/measureRepository.ts @@ -9,6 +9,11 @@ import { logging } from '~/shared/utils/logging' const supabaseGateway = createSupabaseBodyMeasureGateway() +/** + * Creates the measure repository backed by the Supabase gateway. + * + * @returns Repository implementation for measure CRUD operations. + */ export function createMeasureRepository(): BodyMeasureRepository { return { fetchUserBodyMeasures, @@ -18,7 +23,7 @@ export function createMeasureRepository(): BodyMeasureRepository { } } -export async function fetchUserBodyMeasures( +async function fetchUserBodyMeasures( userId: User['uuid'], ): Promise { try { @@ -29,7 +34,7 @@ export async function fetchUserBodyMeasures( } } -export async function insertBodyMeasure( +async function insertBodyMeasure( newBodyMeasure: NewBodyMeasure, ): Promise { try { @@ -40,7 +45,7 @@ export async function insertBodyMeasure( } } -export async function updateBodyMeasure( +async function updateBodyMeasure( bodyMeasureId: BodyMeasure['id'], newBodyMeasure: NewBodyMeasure, ): Promise { @@ -55,7 +60,7 @@ export async function updateBodyMeasure( } } -export async function deleteBodyMeasure( +async function deleteBodyMeasure( bodyMeasureId: BodyMeasure['id'], ): Promise { try { diff --git a/src/modules/measure/infrastructure/supabase/constants.ts b/src/modules/measure/infrastructure/supabase/constants.ts deleted file mode 100644 index f2bd14100..000000000 --- a/src/modules/measure/infrastructure/supabase/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' diff --git a/src/modules/measure/infrastructure/supabase/realtime.ts b/src/modules/measure/infrastructure/supabase/realtime.ts index 50928b842..22d77a879 100644 --- a/src/modules/measure/infrastructure/supabase/realtime.ts +++ b/src/modules/measure/infrastructure/supabase/realtime.ts @@ -2,42 +2,47 @@ import { type BodyMeasure, bodyMeasureSchema, } from '~/modules/measure/domain/measure' -import { SUPABASE_TABLE_BODY_MEASURES } from '~/modules/measure/infrastructure/supabase/constants' import { registerSubapabaseRealtimeCallback } from '~/shared/supabase/supabase' import { logging } from '~/shared/utils/logging' -let initialized = false +const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' -/** - * Sets up granular realtime subscription for body measure changes - * @param onBodyMeasureChange - Callback for granular updates with event details - */ -export function setupBodyMeasureRealtimeSubscription( - onBodyMeasureChange: (event: { - eventType: 'INSERT' | 'UPDATE' | 'DELETE' - old?: BodyMeasure - new?: BodyMeasure - }) => void, -): void { - registerSubapabaseRealtimeCallback( - SUPABASE_TABLE_BODY_MEASURES, - bodyMeasureSchema, - onBodyMeasureChange, - ) -} +export function createMeasureRealtimeService() { + let initialized = false -export function initializeMeasureRealtime(deps: { - refetchBodyMeasures: () => void -}): void { - if (initialized) { - return + /** + * Sets up granular realtime subscription for body measure changes + * @param onBodyMeasureChange - Callback for granular updates with event details + */ + function setupBodyMeasureRealtimeSubscription( + onBodyMeasureChange: (event: { + eventType: 'INSERT' | 'UPDATE' | 'DELETE' + old?: BodyMeasure + new?: BodyMeasure + }) => void, + ): void { + registerSubapabaseRealtimeCallback( + SUPABASE_TABLE_BODY_MEASURES, + bodyMeasureSchema, + onBodyMeasureChange, + ) } - logging.debug(`Measure realtime initialized!`) - initialized = true - registerSubapabaseRealtimeCallback( - SUPABASE_TABLE_BODY_MEASURES, - bodyMeasureSchema, - (event) => { + + /** + * Initializes the body measure realtime subscription once per session. + * + * @param deps - Dependencies used to react to realtime body measure changes. + * @returns Nothing. + */ + function initializeMeasureRealtime(deps: { + refetchBodyMeasures: () => void + }): void { + if (initialized) { + return + } + logging.debug(`Measure realtime initialized!`) + initialized = true + setupBodyMeasureRealtimeSubscription((event) => { logging.debug(`Event:`, event) switch (event.eventType) { @@ -49,6 +54,10 @@ export function initializeMeasureRealtime(deps: { break } } - }, - ) + }) + } + + return { + initializeMeasureRealtime, + } } diff --git a/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts b/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts index 1702a626d..22493a3c7 100644 --- a/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts +++ b/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts @@ -3,13 +3,20 @@ import { type NewBodyMeasure, } from '~/modules/measure/domain/measure' import { type BodyMeasureGateway } from '~/modules/measure/domain/measureGateway' -import { SUPABASE_TABLE_BODY_MEASURES } from '~/modules/measure/infrastructure/supabase/constants' -import { supabaseBodyMeasureMapper } from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' +import { createSupabaseBodyMeasureMapper } from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' import { type User } from '~/modules/user/domain/user' import { supabase } from '~/shared/supabase/supabase' import { wrapErrorWithStack } from '~/shared/utils/errorUtils' import { logging } from '~/shared/utils/logging' +const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' +const supabaseBodyMeasureMapper = createSupabaseBodyMeasureMapper() + +/** + * Creates the Supabase gateway used to persist body measures. + * + * @returns Gateway implementation for body measure persistence operations. + */ export function createSupabaseBodyMeasureGateway(): BodyMeasureGateway { return { fetchUserBodyMeasures, diff --git a/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts b/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts index cd29328a1..819cf6230 100644 --- a/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts +++ b/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts @@ -6,10 +6,9 @@ import { import { type Database } from '~/shared/supabase/database.types' import { parseWithStack } from '~/shared/utils/parseWithStack' -export type InsertBodyMeasureDTO = +type InsertBodyMeasureDTO = Database['public']['Tables']['body_measures']['Insert'] -export type BodyMeasureDTO = - Database['public']['Tables']['body_measures']['Row'] +type BodyMeasureDTO = Database['public']['Tables']['body_measures']['Row'] // Conversion functions function toInsertDTO(newBodyMeasure: NewBodyMeasure): InsertBodyMeasureDTO { @@ -35,7 +34,15 @@ function toDomain(dto: BodyMeasureDTO): BodyMeasure { }) } -export const supabaseBodyMeasureMapper = { - toDomain, - toInsertDTO, +/** + * Creates the mapper responsible for converting body measure data + * between Supabase DTOs and domain entities. + * + * @returns Mapper functions for reading and writing body measure records. + */ +export function createSupabaseBodyMeasureMapper() { + return { + toDomain, + toInsertDTO, + } } diff --git a/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts new file mode 100644 index 000000000..4c7094a7e --- /dev/null +++ b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest' + +import * as measureRepositoryModule from '~/modules/measure/infrastructure/measureRepository' +import * as realtimeModule from '~/modules/measure/infrastructure/supabase/realtime' +import * as gatewayModule from '~/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway' +import * as mapperModule from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' + +describe('measure infrastructure module exports', () => { + it('exports only the measure repository factory', () => { + expect(Object.keys(measureRepositoryModule)).toEqual([ + 'createMeasureRepository', + ]) + }) + + it('exports only the Supabase gateway factory', () => { + expect(Object.keys(gatewayModule)).toEqual([ + 'createSupabaseBodyMeasureGateway', + ]) + }) + + it('exports only the Supabase mapper factory', () => { + expect(Object.keys(mapperModule)).toEqual([ + 'createSupabaseBodyMeasureMapper', + ]) + }) + + it('keeps realtime exports limited to the public factory', () => { + expect(Object.keys(realtimeModule)).toEqual([ + 'createMeasureRealtimeService', + ]) + }) + + it('creates a realtime service with the public initializer', () => { + expect(Object.keys(realtimeModule.createMeasureRealtimeService())).toEqual([ + 'initializeMeasureRealtime', + ]) + }) +})