diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/channel-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/channel-repository-prisma.ts index 1f955d6d..0288ccaf 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/channel-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/channel-repository-prisma.ts @@ -1,11 +1,11 @@ import { PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; +import { IChannelRepository } from '../../app'; import { Channel, ChannelId, ChannelYoutube, - IChannelRepository, OrganizationId, PerformerId, YoutubeChannelId, diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/media-attachment-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/media-attachment-repository-prisma.ts index f5dbf569..9d30d715 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/media-attachment-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/media-attachment-repository-prisma.ts @@ -2,9 +2,8 @@ import { PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; import { getPlaiceholder } from 'plaiceholder'; -import { IStorage } from '../../app'; +import { IMediaAttachmentRepository, IStorage } from '../../app'; import { - IMediaAttachmentRepository, MediaAttachment, MediaAttachmentFilename, MediaAttachmentId, diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/organization-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/organization-repository-prisma.ts index 69b92d67..9d57b333 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/organization-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/organization-repository-prisma.ts @@ -1,9 +1,8 @@ import { Prisma, PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; +import { FindOrganizationParams, IOrganizationRepository } from '../../app'; import { - FindOrganizationParams, - IOrganizationRepository, Organization, OrganizationId, PerformerId, diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/performer-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/performer-repository-prisma.ts index ca68a35f..4144b7a4 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/performer-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/performer-repository-prisma.ts @@ -1,14 +1,8 @@ import { Prisma, PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; -import { - FindPerformerParams, - IPerformerRepository, - Performer, - PerformerId, - unwrap, - YoutubeChannelId, -} from '../../domain'; +import { FindPerformerParams, IPerformerRepository } from '../../app'; +import { Performer, PerformerId, unwrap, YoutubeChannelId } from '../../domain'; import { TYPES } from '../../types'; import { rehydratePerformerFromPrisma } from '../mappers'; diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/repositories.ts b/packages/@neet/vschedule-api/src/adapters/repositories/repositories.ts index a1fc5220..f66d1586 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/repositories.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/repositories.ts @@ -9,7 +9,7 @@ import { IStreamRepository, ITokenRepository, IUserRepository, -} from '../../domain'; +} from '../../app'; import { TYPES } from '../../types'; import { ChannelRepositoryPrisma } from './channel-repository-prisma'; import { MediaAttachmentRepositoryPrisma } from './media-attachment-repository-prisma'; diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-cloud-tasks.ts b/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-cloud-tasks.ts index a5bc3cb8..674f8316 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-cloud-tasks.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-cloud-tasks.ts @@ -2,11 +2,13 @@ import { CloudTasksClient } from '@google-cloud/tasks'; import { google } from '@google-cloud/tasks/build/protos/protos'; import { inject, injectable } from 'inversify'; -import { IConfig, ILogger, utils } from '../../app'; import { + IConfig, + ILogger, IResubscriptionTaskRepository, - ResubscriptionTask, -} from '../../domain'; + utils, +} from '../../app'; +import { ResubscriptionTask } from '../../domain'; import { TYPES } from '../../types'; @injectable() diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-in-memory.ts b/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-in-memory.ts index 70aeeec1..cb99d44a 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-in-memory.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/resubscription-task-repository-in-memory.ts @@ -1,9 +1,7 @@ import { injectable } from 'inversify'; -import { - IResubscriptionTaskRepository, - ResubscriptionTask, -} from '../../domain'; +import { IResubscriptionTaskRepository } from '../../app'; +import { ResubscriptionTask } from '../../domain'; @injectable() export class ResubscriptionTaskRepositoryInMemory diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/stream-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/stream-repository-prisma.ts index 7635106e..4f02a3d4 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/stream-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/stream-repository-prisma.ts @@ -2,13 +2,8 @@ import { PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; import { URL } from 'url'; -import { - IStreamRepository, - ListStreamsParams, - Stream, - StreamId, - unwrap, -} from '../../domain'; +import { IStreamRepository, ListStreamsParams } from '../../app'; +import { Stream, StreamId, unwrap } from '../../domain'; import { TYPES } from '../../types'; import { rehydrateStreamFromPrisma } from '../mappers'; diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/token-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/token-repository-prisma.ts index ee7c35b5..ba965e8f 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/token-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/token-repository-prisma.ts @@ -1,7 +1,8 @@ import { PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; -import { ITokenRepository, Token, TokenId } from '../../domain'; +import { ITokenRepository } from '../../app'; +import { Token, TokenId } from '../../domain'; import { TYPES } from '../../types'; import { rehydrateTokenFromPrisma } from '../mappers'; diff --git a/packages/@neet/vschedule-api/src/adapters/repositories/user-repository-prisma.ts b/packages/@neet/vschedule-api/src/adapters/repositories/user-repository-prisma.ts index e6a2e6d6..dc869973 100644 --- a/packages/@neet/vschedule-api/src/adapters/repositories/user-repository-prisma.ts +++ b/packages/@neet/vschedule-api/src/adapters/repositories/user-repository-prisma.ts @@ -1,7 +1,8 @@ import { PrismaClient } from '@prisma/client'; import { inject, injectable } from 'inversify'; -import { IUserRepository, User, UserEmail, UserId } from '../../domain'; +import { IUserRepository } from '../../app'; +import { User, UserEmail, UserId } from '../../domain'; import { TYPES } from '../../types'; import { rehydrateUserFromPrisma } from '../mappers'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/channel/channel-repository.ts b/packages/@neet/vschedule-api/src/app/channel/channel-repository.ts similarity index 57% rename from packages/@neet/vschedule-api/src/domain/entities/channel/channel-repository.ts rename to packages/@neet/vschedule-api/src/app/channel/channel-repository.ts index 3237c2ee..7bd4dfee 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/channel/channel-repository.ts +++ b/packages/@neet/vschedule-api/src/app/channel/channel-repository.ts @@ -1,9 +1,11 @@ -import { YoutubeChannelId } from '../_shared'; -import { OrganizationId } from '../organization'; -import { PerformerId } from '../performer'; -import { Channel } from './channel'; -import { ChannelId } from './channel-id'; -import { ChannelYoutube } from './channel-youtube'; +import { + Channel, + ChannelId, + ChannelYoutube, + OrganizationId, + PerformerId, + YoutubeChannelId, +} from '../../domain'; export interface IChannelRepository { create(channel: Channel): Promise; diff --git a/packages/@neet/vschedule-api/src/app/channel/index.ts b/packages/@neet/vschedule-api/src/app/channel/index.ts index 497d5ba2..4e6adb89 100644 --- a/packages/@neet/vschedule-api/src/app/channel/index.ts +++ b/packages/@neet/vschedule-api/src/app/channel/index.ts @@ -2,5 +2,6 @@ * @file Automatically generated by barrelsby. */ +export * from './channel-repository'; export * from './request-channel-subscription'; export * from './verify-youtube-channel-subscription'; diff --git a/packages/@neet/vschedule-api/src/app/channel/request-channel-subscription.ts b/packages/@neet/vschedule-api/src/app/channel/request-channel-subscription.ts index 8eff97b6..e8894bbe 100644 --- a/packages/@neet/vschedule-api/src/app/channel/request-channel-subscription.ts +++ b/packages/@neet/vschedule-api/src/app/channel/request-channel-subscription.ts @@ -1,8 +1,18 @@ import { inject, injectable } from 'inversify'; -import { ChannelId, ChannelService, ChannelYoutube } from '../../domain'; +import { ChannelId } from '../../domain'; import { TYPES } from '../../types'; import { IYoutubeWebsubService } from '../_external'; +import { AppError } from '../_shared'; +import { IChannelRepository } from './channel-repository'; + +export class RequestChannelSubscriptionNotFoundError extends AppError { + readonly name = 'RequestChannelSubscriptionNotFoundError'; + + constructor(readonly id: ChannelId) { + super(`Channel with id ${id} was not found`); + } +} export type RequestChannelSubscriptionCommand = { readonly channelId: string; @@ -11,8 +21,8 @@ export type RequestChannelSubscriptionCommand = { @injectable() export class RequestChannelSubscription { constructor( - @inject(ChannelService) - private readonly _channelService: ChannelService, + @inject(TYPES.ChannelRepository) + private readonly _channelRepository: IChannelRepository, @inject(TYPES.YoutubeWebsubService) private readonly _youtubeWebsubService: IYoutubeWebsubService, @@ -20,13 +30,22 @@ export class RequestChannelSubscription { async invoke(params: RequestChannelSubscriptionCommand): Promise { const channelId = new ChannelId(params.channelId); - const channel = await this._channelService.requestSubscription(channelId); + const channel = await this._channelRepository.findById(channelId); + if (channel == null) { + throw new RequestChannelSubscriptionNotFoundError(channelId); + } + + try { + const updatedChannel = channel.requestSubscription(); + await this._channelRepository.update(updatedChannel); - // FIXME リポジトリでやるのが正解な気がする - if (channel instanceof ChannelYoutube) { + // FIXME リポジトリでやるのが正解な気がする await this._youtubeWebsubService.subscribeToChannel( - channel.youtubeChannelId.value, + updatedChannel.youtubeChannelId.value, ); + } catch (error) { + const updatedChannel = channel.resetSubscription(); + await this._channelRepository.update(updatedChannel); } } } diff --git a/packages/@neet/vschedule-api/src/app/channel/verify-youtube-channel-subscription.ts b/packages/@neet/vschedule-api/src/app/channel/verify-youtube-channel-subscription.ts index de551943..8452c8ed 100644 --- a/packages/@neet/vschedule-api/src/app/channel/verify-youtube-channel-subscription.ts +++ b/packages/@neet/vschedule-api/src/app/channel/verify-youtube-channel-subscription.ts @@ -1,11 +1,12 @@ import dayjs from 'dayjs'; import { inject, injectable } from 'inversify'; -import { YoutubeChannelId } from '../../domain'; -import { IChannelRepository } from '../../domain/entities/channel'; -import { ChannelService } from '../../domain/services/channel-service'; +import { ChannelService, YoutubeChannelId } from '../../domain'; import { TYPES } from '../../types'; import { AppError, ILogger } from '../_shared'; +import { IResubscriptionTaskRepository } from '../resubscription-task'; +import { ITokenRepository } from '../token'; +import { IChannelRepository } from './channel-repository'; export class VerifyYoutubeChannelSubscriptionInvalidTopicError extends AppError { public readonly name = 'VerifyYoutubeChannelSubscriptionInvalidTopicError'; @@ -39,6 +40,12 @@ export class VerifyYoutubeChannelSubscription { @inject(TYPES.ChannelRepository) private readonly _channelRepository: IChannelRepository, + @inject(TYPES.TokenRepository) + private readonly _tokenRepository: ITokenRepository, + + @inject(TYPES.ResubscriptionTaskRepository) + private readonly _resubscriptionTaskRepository: IResubscriptionTaskRepository, + @inject(TYPES.Logger) private readonly _logger: ILogger, ) {} @@ -64,11 +71,19 @@ export class VerifyYoutubeChannelSubscription { ); } - await this._channelService.verifySubscription( - channel.id, + const { + channel: updatedChannel, + token, + resubscriptionTask, + } = this._channelService.verifySubscription( + channel, dayjs().add(command.leaseSeconds, 'seconds'), ); + await this._channelRepository.update(updatedChannel); + await this._tokenRepository.create(token); + await this._resubscriptionTaskRepository.create(resubscriptionTask); + this._logger.info( `Scheduled resubscription for ${topic} in ${command.leaseSeconds} secs`, ); diff --git a/packages/@neet/vschedule-api/src/app/index.ts b/packages/@neet/vschedule-api/src/app/index.ts index 43379257..bf2759b6 100644 --- a/packages/@neet/vschedule-api/src/app/index.ts +++ b/packages/@neet/vschedule-api/src/app/index.ts @@ -10,6 +10,7 @@ export * from './factories'; export * from './media-attachment/index'; export * from './organization/index'; export * from './performer/index'; +export * from './resubscription-task/index'; export * from './stream/index'; export * from './token/index'; export * from './user/index'; diff --git a/packages/@neet/vschedule-api/src/app/media-attachment/index.ts b/packages/@neet/vschedule-api/src/app/media-attachment/index.ts index fdfebaeb..5f9a77ee 100644 --- a/packages/@neet/vschedule-api/src/app/media-attachment/index.ts +++ b/packages/@neet/vschedule-api/src/app/media-attachment/index.ts @@ -2,4 +2,5 @@ * @file Automatically generated by barrelsby. */ +export * from './media-attachment-repository'; export * from './show-media-attachment'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/media-attachment/media-attachment-repository.ts b/packages/@neet/vschedule-api/src/app/media-attachment/media-attachment-repository.ts similarity index 68% rename from packages/@neet/vschedule-api/src/domain/entities/media-attachment/media-attachment-repository.ts rename to packages/@neet/vschedule-api/src/app/media-attachment/media-attachment-repository.ts index 5caa50cf..ea79b7be 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/media-attachment/media-attachment-repository.ts +++ b/packages/@neet/vschedule-api/src/app/media-attachment/media-attachment-repository.ts @@ -1,6 +1,8 @@ -import { MediaAttachment } from './media-attachment'; -import { MediaAttachmentFilename } from './media-attachment-filename'; -import { MediaAttachmentId } from './media-attachment-id'; +import { + MediaAttachment, + MediaAttachmentFilename, + MediaAttachmentId, +} from '../../domain'; export interface IMediaAttachmentRepository { findById(id: MediaAttachmentId): Promise; diff --git a/packages/@neet/vschedule-api/src/app/media-attachment/show-media-attachment.ts b/packages/@neet/vschedule-api/src/app/media-attachment/show-media-attachment.ts index 708de478..7caf3344 100644 --- a/packages/@neet/vschedule-api/src/app/media-attachment/show-media-attachment.ts +++ b/packages/@neet/vschedule-api/src/app/media-attachment/show-media-attachment.ts @@ -1,12 +1,9 @@ import { inject, injectable } from 'inversify'; -import { - IMediaAttachmentRepository, - MediaAttachment, - MediaAttachmentFilename, -} from '../../domain'; +import { MediaAttachment, MediaAttachmentFilename } from '../../domain'; import { TYPES } from '../../types'; import { AppError } from '../_shared'; +import { IMediaAttachmentRepository } from './media-attachment-repository'; export class ShowMediaAttachmentNotFoundError extends AppError { public readonly name = 'ShowMediaAttachmentNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/app/organization/index.ts b/packages/@neet/vschedule-api/src/app/organization/index.ts index fdd8c7d9..d81fe7b2 100644 --- a/packages/@neet/vschedule-api/src/app/organization/index.ts +++ b/packages/@neet/vschedule-api/src/app/organization/index.ts @@ -5,5 +5,6 @@ export * from './list-organizations'; export * from './organization-factory-impl'; export * from './organization-query-service'; +export * from './organization-repository'; export * from './show-organization'; export * from './upsert-organization'; diff --git a/packages/@neet/vschedule-api/src/app/organization/organization-factory-impl.ts b/packages/@neet/vschedule-api/src/app/organization/organization-factory-impl.ts index 1a79e450..aadcdcb8 100644 --- a/packages/@neet/vschedule-api/src/app/organization/organization-factory-impl.ts +++ b/packages/@neet/vschedule-api/src/app/organization/organization-factory-impl.ts @@ -5,7 +5,6 @@ import fetch from 'node-fetch'; import sharp from 'sharp'; import { - IMediaAttachmentRepository, IOrganizationFactory, MediaAttachmentFilename, Organization, @@ -15,6 +14,7 @@ import { import { TYPES } from '../../types'; import { IYoutubeApiService } from '../_external'; import { AppError, ILogger, UnexpectedError } from '../_shared'; +import { IMediaAttachmentRepository } from '../media-attachment'; export class OrganizationFactoryChannelNotFoundError extends AppError { public readonly name = 'OrganizationFactoryChannelNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/organization/organization-repository.ts b/packages/@neet/vschedule-api/src/app/organization/organization-repository.ts similarity index 68% rename from packages/@neet/vschedule-api/src/domain/entities/organization/organization-repository.ts rename to packages/@neet/vschedule-api/src/app/organization/organization-repository.ts index 5e62de54..8b56729c 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/organization/organization-repository.ts +++ b/packages/@neet/vschedule-api/src/app/organization/organization-repository.ts @@ -1,12 +1,14 @@ -import { YoutubeChannelId } from '../_shared'; -import { PerformerId } from '../performer'; -import { Organization } from './organization'; -import { OrganizationId } from './organization-id'; +import { + Organization, + OrganizationId, + PerformerId, + YoutubeChannelId, +} from '../../domain'; -export interface FindOrganizationParams { +export type FindOrganizationParams = { readonly limit?: number; readonly offset?: number; -} +}; export interface IOrganizationRepository { create(organization: Organization): Promise; diff --git a/packages/@neet/vschedule-api/src/app/organization/upsert-organization.ts b/packages/@neet/vschedule-api/src/app/organization/upsert-organization.ts index 7a12d2d1..ab09811c 100644 --- a/packages/@neet/vschedule-api/src/app/organization/upsert-organization.ts +++ b/packages/@neet/vschedule-api/src/app/organization/upsert-organization.ts @@ -3,7 +3,6 @@ import { inject, injectable } from 'inversify'; import { IOrganizationFactory, - IOrganizationRepository, Organization, OrganizationDescription, OrganizationName, @@ -12,6 +11,7 @@ import { } from '../../domain'; import { TYPES } from '../../types'; import { ILogger } from '../_shared'; +import { IOrganizationRepository } from './organization-repository'; export type UpsertOrganizationCommand = { readonly name: string; diff --git a/packages/@neet/vschedule-api/src/app/performer/index.ts b/packages/@neet/vschedule-api/src/app/performer/index.ts index d78816b0..cdb5517a 100644 --- a/packages/@neet/vschedule-api/src/app/performer/index.ts +++ b/packages/@neet/vschedule-api/src/app/performer/index.ts @@ -5,5 +5,6 @@ export * from './list-performers'; export * from './performer-factory-impl'; export * from './performer-query-service'; +export * from './performer-repository'; export * from './show-performer'; export * from './upsert-performer'; diff --git a/packages/@neet/vschedule-api/src/app/performer/performer-factory-impl.ts b/packages/@neet/vschedule-api/src/app/performer/performer-factory-impl.ts index 6b020f90..aae3f07e 100644 --- a/packages/@neet/vschedule-api/src/app/performer/performer-factory-impl.ts +++ b/packages/@neet/vschedule-api/src/app/performer/performer-factory-impl.ts @@ -5,7 +5,6 @@ import fetch from 'node-fetch'; import sharp from 'sharp'; import { - IMediaAttachmentRepository, IPerformerFactory, MediaAttachmentFilename, Performer, @@ -15,6 +14,7 @@ import { import { TYPES } from '../../types'; import { IYoutubeApiService } from '../_external'; import { AppError, ILogger, UnexpectedError } from '../_shared'; +import { IMediaAttachmentRepository } from '../media-attachment'; export class PerformerFactoryChannelNotFoundError extends AppError { public readonly name = 'PerformerFactoryChannelNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/performer/performer-repository.ts b/packages/@neet/vschedule-api/src/app/performer/performer-repository.ts similarity index 78% rename from packages/@neet/vschedule-api/src/domain/entities/performer/performer-repository.ts rename to packages/@neet/vschedule-api/src/app/performer/performer-repository.ts index bad6c3cf..cd143f37 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/performer/performer-repository.ts +++ b/packages/@neet/vschedule-api/src/app/performer/performer-repository.ts @@ -1,6 +1,4 @@ -import { YoutubeChannelId } from '../_shared'; -import { Performer } from './performer'; -import { PerformerId } from './performer-id'; +import { Performer, PerformerId, YoutubeChannelId } from '../../domain'; export interface FindPerformerParams { readonly limit?: number; diff --git a/packages/@neet/vschedule-api/src/app/performer/upsert-performer.ts b/packages/@neet/vschedule-api/src/app/performer/upsert-performer.ts index 45f04e56..0156ef8b 100644 --- a/packages/@neet/vschedule-api/src/app/performer/upsert-performer.ts +++ b/packages/@neet/vschedule-api/src/app/performer/upsert-performer.ts @@ -3,7 +3,6 @@ import { inject, injectable } from 'inversify'; import { IPerformerFactory, - IPerformerRepository, OrganizationId, OrganizationService, Performer, @@ -13,7 +12,17 @@ import { YoutubeChannelId, } from '../../domain'; import { TYPES } from '../../types'; -import { ILogger } from '../_shared'; +import { AppError, ILogger } from '../_shared'; +import { IOrganizationRepository } from '../organization'; +import { IPerformerRepository } from './performer-repository'; + +export class UpsertPerformerOrganizationNotFoundError extends AppError { + readonly name = 'UpsertPerformerOrganizationNotFoundError'; + + constructor(id: OrganizationId) { + super(`Organization with ID ${id} was not found`); + } +} export type UpsertPerformerCommand = { readonly youtubeChannelId: string; @@ -37,6 +46,9 @@ export class UpsertPerformer { @inject(OrganizationService) private readonly _organizationService: OrganizationService, + @inject(TYPES.OrganizationRepository) + private readonly _organizationRepository: IOrganizationRepository, + @inject(TYPES.Logger) private readonly _logger: ILogger, ) {} @@ -53,10 +65,18 @@ export class UpsertPerformer { : await this.updatePerformer(existingPerformer, command); if (command.organizationId != null) { - await this._organizationService.addPerformer( + const organizationId = new OrganizationId(command.organizationId); + const existingOrganization = await this._organizationRepository.findById( + organizationId, + ); + if (existingOrganization == null) { + throw new UpsertPerformerOrganizationNotFoundError(organizationId); + } + const updatedPerformer = this._organizationService.addPerformer( performer, - new OrganizationId(command.organizationId), + existingOrganization, ); + await this._performerRepository.update(updatedPerformer); } } diff --git a/packages/@neet/vschedule-api/src/app/resubscription-task/index.ts b/packages/@neet/vschedule-api/src/app/resubscription-task/index.ts new file mode 100644 index 00000000..f8d54e78 --- /dev/null +++ b/packages/@neet/vschedule-api/src/app/resubscription-task/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './resubscription-task-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/resubscription-task-repository.ts b/packages/@neet/vschedule-api/src/app/resubscription-task/resubscription-task-repository.ts similarity index 63% rename from packages/@neet/vschedule-api/src/domain/entities/resubscription-task/resubscription-task-repository.ts rename to packages/@neet/vschedule-api/src/app/resubscription-task/resubscription-task-repository.ts index 53c1afd4..e47a4dcb 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/resubscription-task-repository.ts +++ b/packages/@neet/vschedule-api/src/app/resubscription-task/resubscription-task-repository.ts @@ -1,4 +1,4 @@ -import { ResubscriptionTask } from './resubscription-task'; +import { ResubscriptionTask } from '../../domain'; export interface IResubscriptionTaskRepository { create(task: ResubscriptionTask): Promise; diff --git a/packages/@neet/vschedule-api/src/app/stream/index.ts b/packages/@neet/vschedule-api/src/app/stream/index.ts index 84957d34..21878200 100644 --- a/packages/@neet/vschedule-api/src/app/stream/index.ts +++ b/packages/@neet/vschedule-api/src/app/stream/index.ts @@ -7,4 +7,5 @@ export * from './remove-stream'; export * from './show-stream'; export * from './stream-factory-impl'; export * from './stream-query-service'; +export * from './stream-repository'; export * from './upsert-stream'; diff --git a/packages/@neet/vschedule-api/src/app/stream/remove-stream.ts b/packages/@neet/vschedule-api/src/app/stream/remove-stream.ts index 39937ee8..c4c47ed0 100644 --- a/packages/@neet/vschedule-api/src/app/stream/remove-stream.ts +++ b/packages/@neet/vschedule-api/src/app/stream/remove-stream.ts @@ -1,9 +1,9 @@ import { inject, injectable } from 'inversify'; import { URL } from 'url'; -import { IStreamRepository } from '../../domain'; import { TYPES } from '../../types'; import { AppError, ILogger } from '../_shared'; +import { IStreamRepository } from './stream-repository'; export class RemoveStreamNotFoundError extends AppError { public readonly name = 'RemoveStreamNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/app/stream/stream-factory-impl.ts b/packages/@neet/vschedule-api/src/app/stream/stream-factory-impl.ts index 908e63ca..0a67a332 100644 --- a/packages/@neet/vschedule-api/src/app/stream/stream-factory-impl.ts +++ b/packages/@neet/vschedule-api/src/app/stream/stream-factory-impl.ts @@ -5,11 +5,7 @@ import fetch from 'node-fetch'; import sharp from 'sharp'; import { - IChannelRepository, - IMediaAttachmentRepository, - IPerformerRepository, IStreamFactory, - IStreamRepository, MediaAttachment, MediaAttachmentFilename, Performer, @@ -22,6 +18,10 @@ import { import { TYPES } from '../../types'; import { IYoutubeApiService, Video } from '../_external'; import { AppError } from '../_shared'; +import { IChannelRepository } from '../channel'; +import { IMediaAttachmentRepository } from '../media-attachment'; +import { IPerformerRepository } from '../performer'; +import { IStreamRepository } from './stream-repository'; const YOUTUBE_CHANNEL_REGEXP = /https:\/\/www\.youtube\.com\/channel\/(.+?)(\/|\s|\n|\?)/g; diff --git a/packages/@neet/vschedule-api/src/domain/entities/stream/stream-repository.ts b/packages/@neet/vschedule-api/src/app/stream/stream-repository.ts similarity index 78% rename from packages/@neet/vschedule-api/src/domain/entities/stream/stream-repository.ts rename to packages/@neet/vschedule-api/src/app/stream/stream-repository.ts index 1af1269a..608ac69a 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/stream/stream-repository.ts +++ b/packages/@neet/vschedule-api/src/app/stream/stream-repository.ts @@ -1,7 +1,7 @@ import { URL } from 'url'; -import { Stream } from './stream'; -import { StreamId } from './stream-id'; +import { Stream } from '../../domain/entities/stream/stream'; +import { StreamId } from '../../domain/entities/stream/stream-id'; export type ListStreamsParams = { readonly limit?: number; diff --git a/packages/@neet/vschedule-api/src/app/stream/upsert-stream.ts b/packages/@neet/vschedule-api/src/app/stream/upsert-stream.ts index 8a7496eb..2b9977a8 100644 --- a/packages/@neet/vschedule-api/src/app/stream/upsert-stream.ts +++ b/packages/@neet/vschedule-api/src/app/stream/upsert-stream.ts @@ -1,10 +1,11 @@ import { inject, injectable } from 'inversify'; -import { IStreamFactory, IStreamRepository } from '../../domain'; +import { IStreamFactory } from '../../domain'; import { TYPES } from '../../types'; import { ILogger, UnexpectedError } from '../_shared'; import { StreamDto } from '../dto'; import { IStreamQueryService } from './stream-query-service'; +import { IStreamRepository } from './stream-repository'; export type UpsertStreamCommand = { readonly videoId: string; diff --git a/packages/@neet/vschedule-api/src/app/token/drain-token.ts b/packages/@neet/vschedule-api/src/app/token/drain-token.ts index f7f73d44..2e10c61d 100644 --- a/packages/@neet/vschedule-api/src/app/token/drain-token.ts +++ b/packages/@neet/vschedule-api/src/app/token/drain-token.ts @@ -1,8 +1,9 @@ import { inject, injectable } from 'inversify'; -import { ITokenRepository, TokenId } from '../../domain'; +import { TokenId } from '../../domain'; import { TYPES } from '../../types'; import { AppError, ILogger } from '../_shared'; +import { ITokenRepository } from './token-repository'; export class DrainTokenNotFoundError extends AppError { public readonly name = 'DrainTokenNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/app/token/index.ts b/packages/@neet/vschedule-api/src/app/token/index.ts index a46ab777..f6413132 100644 --- a/packages/@neet/vschedule-api/src/app/token/index.ts +++ b/packages/@neet/vschedule-api/src/app/token/index.ts @@ -3,3 +3,4 @@ */ export * from './drain-token'; +export * from './token-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/token/token-repository.ts b/packages/@neet/vschedule-api/src/app/token/token-repository.ts similarity index 57% rename from packages/@neet/vschedule-api/src/domain/entities/token/token-repository.ts rename to packages/@neet/vschedule-api/src/app/token/token-repository.ts index 95d2a090..9b746e0c 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/token/token-repository.ts +++ b/packages/@neet/vschedule-api/src/app/token/token-repository.ts @@ -1,5 +1,5 @@ -import { Token } from './token'; -import { TokenId } from './token-id'; +import { Token } from '../../domain/entities/token/token'; +import { TokenId } from '../../domain/entities/token/token-id'; export interface ITokenRepository { create(token: Token): Promise; diff --git a/packages/@neet/vschedule-api/src/app/user/create-user.ts b/packages/@neet/vschedule-api/src/app/user/create-user.ts index d157174f..52f13c7c 100644 --- a/packages/@neet/vschedule-api/src/app/user/create-user.ts +++ b/packages/@neet/vschedule-api/src/app/user/create-user.ts @@ -1,9 +1,10 @@ import { hashSync } from 'bcryptjs'; import { inject, injectable } from 'inversify'; -import { IUserRepository, User, UserEmail } from '../../domain'; +import { User, UserEmail } from '../../domain'; import { TYPES } from '../../types'; import { AppError, IConfig, ILogger } from '../_shared'; +import { IUserRepository } from './user-repository'; export class CreateUserAlreadyExists extends AppError { readonly name = 'CreateUserAlreadyExists'; diff --git a/packages/@neet/vschedule-api/src/app/user/index.ts b/packages/@neet/vschedule-api/src/app/user/index.ts index 6ff0e6ce..8036aa2a 100644 --- a/packages/@neet/vschedule-api/src/app/user/index.ts +++ b/packages/@neet/vschedule-api/src/app/user/index.ts @@ -5,3 +5,4 @@ export * from './create-user'; export * from './login'; export * from './show-user'; +export * from './user-repository'; diff --git a/packages/@neet/vschedule-api/src/app/user/login.ts b/packages/@neet/vschedule-api/src/app/user/login.ts index 89ad897d..55217f94 100644 --- a/packages/@neet/vschedule-api/src/app/user/login.ts +++ b/packages/@neet/vschedule-api/src/app/user/login.ts @@ -1,8 +1,9 @@ import { inject, injectable } from 'inversify'; -import { IUserRepository, User, UserEmail } from '../../domain'; +import { User, UserEmail } from '../../domain'; import { TYPES } from '../../types'; import { AppError, ILogger } from '../_shared'; +import { IUserRepository } from './user-repository'; export class LoginAccountNotFoundError extends AppError { public readonly name = 'LoginAccountNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/app/user/show-user.ts b/packages/@neet/vschedule-api/src/app/user/show-user.ts index 3955e5b9..a1f44a2d 100644 --- a/packages/@neet/vschedule-api/src/app/user/show-user.ts +++ b/packages/@neet/vschedule-api/src/app/user/show-user.ts @@ -1,8 +1,9 @@ import { inject, injectable } from 'inversify'; -import { IUserRepository, User, UserId } from '../../domain'; +import { User, UserId } from '../../domain'; import { TYPES } from '../../types'; import { AppError } from '../_shared'; +import { IUserRepository } from './user-repository'; export class ShowUserNotFoundError extends AppError { public readonly name = 'ShowUserNotFoundError'; diff --git a/packages/@neet/vschedule-api/src/app/user/user-repository.ts b/packages/@neet/vschedule-api/src/app/user/user-repository.ts new file mode 100644 index 00000000..09736cdd --- /dev/null +++ b/packages/@neet/vschedule-api/src/app/user/user-repository.ts @@ -0,0 +1,9 @@ +import { User } from '../../domain/entities/user/user'; +import { UserEmail } from '../../domain/entities/user/user-email'; +import { UserId } from '../../domain/entities/user/user-id'; + +export interface IUserRepository { + findById(id: UserId): Promise; + findByEmail(email: UserEmail): Promise; + create(user: User): Promise; +} diff --git a/packages/@neet/vschedule-api/src/domain/entities/channel/channel-youtube.ts b/packages/@neet/vschedule-api/src/domain/entities/channel/channel-youtube.ts index 5a834383..a7380eb6 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/channel/channel-youtube.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/channel/channel-youtube.ts @@ -47,6 +47,13 @@ export class ChannelYoutube extends mixins implements IChannel { return this._props.youtubeChannelId; } + public resetSubscription(): ChannelYoutube { + const props = produce(this._props, (draft) => { + draft.status = ChannelStatus.UNSET; + }); + return new ChannelYoutube(props); + } + public requestSubscription(): ChannelYoutube { const props = produce(this._props, (draft) => { draft.status = ChannelStatus.REQUESTED; diff --git a/packages/@neet/vschedule-api/src/domain/entities/channel/index.ts b/packages/@neet/vschedule-api/src/domain/entities/channel/index.ts index 21550547..7b1199c5 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/channel/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/channel/index.ts @@ -7,6 +7,5 @@ export * from './channel-base'; export * from './channel-description'; export * from './channel-id'; export * from './channel-name'; -export * from './channel-repository'; export * from './channel-status'; export * from './channel-youtube'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/media-attachment/index.ts b/packages/@neet/vschedule-api/src/domain/entities/media-attachment/index.ts index 76cbefb8..a6736d33 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/media-attachment/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/media-attachment/index.ts @@ -6,5 +6,4 @@ export * from './media-attachment'; export * from './media-attachment-bucket'; export * from './media-attachment-filename'; export * from './media-attachment-id'; -export * from './media-attachment-repository'; export * from './media-attachment-size'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/organization/index.ts b/packages/@neet/vschedule-api/src/domain/entities/organization/index.ts index 41431fbe..0552340d 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/organization/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/organization/index.ts @@ -7,4 +7,3 @@ export * from './organization-description'; export * from './organization-factory'; export * from './organization-id'; export * from './organization-name'; -export * from './organization-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/performer/index.ts b/packages/@neet/vschedule-api/src/domain/entities/performer/index.ts index 7e7513af..4d3fea6d 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/performer/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/performer/index.ts @@ -7,4 +7,3 @@ export * from './performer-description'; export * from './performer-factory'; export * from './performer-id'; export * from './performer-name'; -export * from './performer-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/index.ts b/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/index.ts index 4b0ebc5e..24be45a5 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/resubscription-task/index.ts @@ -3,4 +3,3 @@ */ export * from './resubscription-task'; -export * from './resubscription-task-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/stream/index.ts b/packages/@neet/vschedule-api/src/domain/entities/stream/index.ts index a70abc47..7693e427 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/stream/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/stream/index.ts @@ -6,5 +6,4 @@ export * from './stream'; export * from './stream-description'; export * from './stream-factory'; export * from './stream-id'; -export * from './stream-repository'; export * from './stream-title'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/token/index.ts b/packages/@neet/vschedule-api/src/domain/entities/token/index.ts index ef12ff84..dbd506ef 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/token/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/token/index.ts @@ -4,4 +4,3 @@ export * from './token'; export * from './token-id'; -export * from './token-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/user/index.ts b/packages/@neet/vschedule-api/src/domain/entities/user/index.ts index b32f7cde..f52cce5d 100644 --- a/packages/@neet/vschedule-api/src/domain/entities/user/index.ts +++ b/packages/@neet/vschedule-api/src/domain/entities/user/index.ts @@ -5,4 +5,3 @@ export * from './user'; export * from './user-email'; export * from './user-id'; -export * from './user-repository'; diff --git a/packages/@neet/vschedule-api/src/domain/entities/user/user-repository.ts b/packages/@neet/vschedule-api/src/domain/entities/user/user-repository.ts deleted file mode 100644 index bfba0d8c..00000000 --- a/packages/@neet/vschedule-api/src/domain/entities/user/user-repository.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { User } from './user'; -import { UserEmail } from './user-email'; -import { UserId } from './user-id'; - -export interface IUserRepository { - findById(id: UserId): Promise; - findByEmail(email: UserEmail): Promise; - create(user: User): Promise; -} diff --git a/packages/@neet/vschedule-api/src/domain/services/channel-service.ts b/packages/@neet/vschedule-api/src/domain/services/channel-service.ts index b35424d2..08581798 100644 --- a/packages/@neet/vschedule-api/src/domain/services/channel-service.ts +++ b/packages/@neet/vschedule-api/src/domain/services/channel-service.ts @@ -1,15 +1,11 @@ import { Dayjs } from 'dayjs'; -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; -import { TYPES } from '../../types'; import { DomainError } from '../_core'; import { Channel, ChannelId, ChannelYoutube, - IChannelRepository, - IResubscriptionTaskRepository, - ITokenRepository, ResubscriptionTask, Token, } from '../entities'; @@ -30,57 +26,34 @@ export class ChannelServiceChannelNotRequestedError extends DomainError { } } +export type VerifySubscriptionResult = { + channel: Channel; + token: Token; + resubscriptionTask: ResubscriptionTask; +}; + @injectable() export class ChannelService { - public constructor( - @inject(TYPES.ChannelRepository) - private readonly _channelRepository: IChannelRepository, - - @inject(TYPES.ResubscriptionTaskRepository) - private readonly _resubscriptionTaskRepository: IResubscriptionTaskRepository, - - @inject(TYPES.TokenRepository) - private readonly _tokenRepository: ITokenRepository, - ) {} - - public async requestSubscription(channelId: ChannelId): Promise { - const channel = await this._channelRepository.findById(channelId); - if (channel == null) { - throw new ChannelServiceChannelNotFoundError(channelId); - } - - if (channel instanceof ChannelYoutube) { - const newChannel = channel.requestSubscription(); - await this._channelRepository.update(newChannel); - return channel; - } - throw new Error('unreachable'); - } - - public async verifySubscription( - channelId: ChannelId, + public verifySubscription( + channel: Channel, scheduledAt: Dayjs, - ): Promise { - const channel = await this._channelRepository.findById(channelId); - if (channel == null) { - throw new ChannelServiceChannelNotFoundError(channelId); - } + ): VerifySubscriptionResult { if (!channel.isRequested()) { - throw new ChannelServiceChannelNotRequestedError(channelId); + throw new ChannelServiceChannelNotRequestedError(channel.id); } if (channel instanceof ChannelYoutube) { const newChannel = channel.verifySubscription(); - await this._channelRepository.update(newChannel); - const token = Token.create(); const resubscriptionTask = ResubscriptionTask.create({ token, scheduledAt, channelId: newChannel.id, }); - await this._tokenRepository.create(token); - await this._resubscriptionTaskRepository.create(resubscriptionTask); + + return { channel: newChannel, token, resubscriptionTask }; } + + throw new Error('Unreachable'); } } diff --git a/packages/@neet/vschedule-api/src/domain/services/organization-service.ts b/packages/@neet/vschedule-api/src/domain/services/organization-service.ts index 6e9aa71e..626b457b 100644 --- a/packages/@neet/vschedule-api/src/domain/services/organization-service.ts +++ b/packages/@neet/vschedule-api/src/domain/services/organization-service.ts @@ -1,58 +1,18 @@ -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; -import { TYPES } from '../../types'; -import { DomainError } from '../_core'; -import { - IOrganizationRepository, - IPerformerRepository, - OrganizationId, - Performer, - PerformerId, -} from '../entities'; - -export class AddPerformerToOrganizationServiceNotFoundError extends DomainError { - public readonly name = 'AddPerformerToOrganizationServiceNotFoundError'; - - constructor( - public readonly performerId: PerformerId, - public readonly organizationId: OrganizationId, - ) { - super( - `Performer ${performerId} tried to join non-existing organization ${organizationId}`, - ); - } -} +import { Organization, Performer } from '../entities'; @injectable() export class OrganizationService { - public constructor( - @inject(TYPES.OrganizationRepository) - private readonly _organizationRepository: IOrganizationRepository, - - @inject(TYPES.PerformerRepository) - private readonly _performerRepository: IPerformerRepository, - ) {} - - public async addPerformer( + public addPerformer( performer: Performer, - organizationId: OrganizationId, - ): Promise { - const organization = await this._organizationRepository.findById( - organizationId, - ); - - if (organization == null) { - throw new AddPerformerToOrganizationServiceNotFoundError( - performer.id, - organizationId, - ); - } - + organization: Organization, + ): Performer { if (performer.hasJoinedToOrganization(organization.id)) { - return; + return performer; } - const newPerformer = performer.joinOrganization(organizationId); - await this._performerRepository.update(newPerformer); + const newPerformer = performer.joinOrganization(organization.id); + return newPerformer; } }