メインコンテンツまでスキップ
バージョン: 0.13.0 (Previous)

🛣️ Profile ルート

Profile ルートは Express ミドルウェアではなく SDK コンポーザーです。すべてのルートは最初に認証を行います。以下のルートでは、正確な第 2 アクセス規則を示し、orThrow またはレガシーターミネーターを通じてソースエラーを対応付けます。

一覧

ルートメソッド/プロトコルパススキーマバリデーター成功ステータス
createProfileRoutePOST / HTTP/profilescreateProfileSchemaisAuその後ticated(), some( checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'identityId']) )200
getProfileRouteGET / HTTP/profiles/:profileIdgetProfileSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
findProfilesRouteGET / HTTP/profilesfindProfilesSchemaisAuその後ticated(), checkIdentityType(['admin'])200
updateProfileRoutePATCH / HTTP/profiles/:profileIdupdateProfileSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
deleteProfileRouteDELETE / HTTP/profiles/:profileIddeleteProfileSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
getAvatarUploadUrlRouteGET / HTTP/profiles/:profileId/avatar-upload-urlgetSignedImageUploadUrlSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
findProfilesByIdentityIdRouteGET / HTTP/profiles/identities/:identityIdfindByIdentityIdSchemaisAuその後ticated(), isSelf(['params', 'requestParams', 'identityId'])200
createProfileFollowRoutePUT / HTTP/profiles/:profileId/profile-follows/:followProfileIdcreateProfileFollowSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteProfileFollowRouteDELETE / HTTP/profiles/:profileId/profile-follows/:followProfileIddeleteProfileFollowSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
getProfileFollowersRouteGET / HTTP/profiles/:profileId/followersgetProfileFollowersSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )200
createOrganizationFollowRoutePUT / HTTP/profiles/:profileId/organization-follows/:followOrganizationIdcreateOrganizationFollowSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteOrganizationFollowRouteDELETE / HTTP/profiles/:profileId/organization-follows/:followOrganizationIddeleteOrganizationFollowSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
createProductLikeRoutePUT / HTTP/profiles/:profileId/product-likes/:likeProductIdcreateProductLikeSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204
deleteProductLikeRouteDELETE / HTTP/profiles/:profileId/product-likes/:likeProductIddeleteProductLikeSchemaisAuその後ticated(), some( checkIdentityType(['admin']), ownsProfile(['params', 'requestParams', 'profileId']) )204

詳細

createProfileRoute

実装

エンドポイント: POST /profiles

アクセス: 認証済み管理者、または JSON 本文内のアイデンティティ。

リクエスト: createProfileSchema, application/json 本文。

パイプライン: createProfile, getProfileById, アバター正規化、raw-document 正規化、orThrow

成功: statusCode 記述子なしで正規化済み Profile データを返します。

失敗: レガシーの作成/読み込みハンドラーは 400404500 を返すことがあります。orThrow にはローカルエラー対応付けがないため、それらのエラーは伝播します。

完全なソースを表示
export const createProfileRoute = withRoute({
handler: compose(
withLogging(createProfile),
flatMapAsync(withLogging(getProfileByIdHandler)),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(withLogging(orThrow([], [['context', 'data', 'normalizedProfile']])))
),
method: 'POST',
path: '/profiles',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestBody', 'identityId'])
),
],
});

getProfileRoute

実装

エンドポイント: GET /profiles/:profileId

アクセス: 認証済み管理者または所有者。

リクエスト: パス profileId。参照: getProfileSchema.

パイプライン: getProfileById, アバターおよび raw-document 正規化、orThrow

成功: 正規化済み Profile JSON。

失敗: ソースの Profile 検索失敗。

完全なソースを表示
export const getProfileRoute = withRoute({
handler: compose(
withLogging(getProfileByIdHandler),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(withLogging(orThrow([], [['context', 'data', 'normalizedProfile']])))
),
method: 'GET',
path: '/profiles/:profileId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

findProfilesRoute

実装

エンドポイント: GET /profiles

アクセス: 認証済み管理者。

リクエスト: アイデンティティ/名前フィルターと共有ページネーションクエリパラメーター。参照: findProfilesSchema.

パイプライン: findProfiles, ページネーション、アバター/ドキュメント正規化。

成功: statusCode 記述子なしの { data, metadata: { pagination } }

失敗: レガシーのリストハンドラーは 500 を返すことがあります。このルートにはローカルエラー対応付けがありません。

完全なソースを表示
export const findProfilesRoute = withRoute({
handler: compose(
withPagination(withLogging(findProfilesHandler)),
flatMapAsync(
applyPayloadArgs(
normalizeAvatarsOfOwners,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profiles', 'data'],
],
'profilesWithAvatarUrl'
)
),
flatMapAsync(
applyPayloadArgs(
normalizeDocuments,
[['context', 'data', 'profilesWithAvatarUrl']],
'normalizedProfiles'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedProfiles'],
['context', 'data', 'profiles', 'metadata'],
],
'normalizedProfilesWithPagination'
)
),
lift(
withLogging(
orThrow([], [['context', 'data', 'normalizedProfilesWithPagination']])
)
)
),
method: 'GET',
path: '/profiles',
validators: [isAuその後ticated(), checkIdentityType(['admin'])],
});

updateProfileRoute

実装

エンドポイント: PATCH /profiles/:profileId

アクセス: 認証済み管理者または所有者。

リクエスト: パス ID と JSON 本文。参照: updateProfileSchema.

パイプライン: 以前の Profile を読み込み、 updateProfile, 再読み込みし、置換済みアバターを条件付きで削除して正規化します。

成功: 正規化済み Profile JSON。

失敗: 404 Profile、500 ファイルストレージ。

完全なソースを表示
export const updateProfileRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profileBefore'
)
),

flatMapAsync(withLogging(updateProfile)),
flatMapAsync(withLogging(getProfileByIdHandler)),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteAvatarIfReplaced,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profileBefore', 'avatar'],
['context', 'data', 'profile', 'avatar'],
],
// TODO: Remove this pointless key
'x'
)
)
),

flatMapAsync(
applyPayloadArgs(
normalizeAvatarOfOwner,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile'],
],
'profileWithAvatarUrl'
)
),

flatMapAsync(
applyPayloadArgs(
normalizeRawDocument,
[['context', 'data', 'profileWithAvatarUrl']],
'normalizedProfile'
)
),

lift(
withLogging(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[FileStorageServiceError, 500],
],
[['context', 'data', 'normalizedProfile']]
)
)
)
),
method: 'PATCH',
path: '/profiles/:profileId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProfileRoute

実装

エンドポイント: DELETE /profiles/:profileId

アクセス: 認証済み管理者または所有者。

リクエスト: パス ID。

パイプライン: getProfileById, deleteProfile, deleteAvatar, deleteProfileTerminator.

成功: 204.

失敗: レガシーの読み込み/削除エラーとアバター保存エラーは伝播します。data.deleteProfile がない場合、ターミネーターも 500 をスローします。

完全なソースを表示
export const deleteProfileRoute = withRoute({
handler: compose(
withLogging(getProfileByIdHandler),
flatMapAsync(withLogging(deleteProfile)),
flatMapAsync(
withLogging(
applyPayloadArgs(
deleteAvatar,
[
['context', 'fileStorageDriver'],
['context', 'data', 'profile', 'avatar'],
],
'wasAvatarDeleted'
)
)
),
lift(withLogging(deleteProfileTerminator))
),
method: 'DELETE',
path: '/profiles/:profileId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

getAvatarUploadUrlRoute

実装

エンドポイント: GET /profiles/:profileId/avatar-upload-url

アクセス: 認証済み管理者または所有者。

リクエスト: 共有の署名付き画像クエリ契約(contentTypecontentLength)。

パイプライン: generateSignedAvatarUploadUrl, orThrow.

成功: 200

失敗: ファイルストレージエラーは 500 に対応付けられます。

完全なソースを表示
export const getAvatarUploadUrlRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(generateSignedAvatarUploadUrl, [
['context', 'fileStorageDriver'],
['params', 'requestQuery', 'contentType'],
['params', 'requestQuery', 'contentLength'],
])
),
lift(orThrow([[FileStorageServiceError, 500]], [['context', 'data'], 200]))
),
method: 'GET',
path: '/profiles/:profileId/avatar-upload-url',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

findProfilesByIdentityIdRoute

実装

エンドポイント: GET /profiles/identities/:identityId

アクセス: 認証済みの本人。

リクエスト: 共有アイデンティティパスパラメーターと共有ページネーションクエリ。

パイプライン: アイデンティティフィルター、_id なしの検索オプション、ページネーション済み findProfiles, アバター正規化、レスポンス組み立て。

成功: statusCode 記述子なしの { data, metadata: { pagination } }

失敗: ProfileDbBlockErrorAvatarBlockError500 に対応付けられます。

完全なソースを表示
export const findProfilesByIdentityIdRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
buildIdentityIdFilter,
[['params', 'requestParams', 'identityId']],
'filter'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(buildWithoutMongoIdFindOptions, [], 'options')
)
),
flatMapAsync(
withPagination(
withLogging(
applyPayloadArgs(
findProfiles,
[
['context', 'db', 'profiles'],
['context', 'data', 'filter'],
['context', 'data', 'options'],
],
'paginatedProfiles'
)
)
)
),
flatMapAsync(
applyPayloadArgs(
normalizeAvatarsOfOwners,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedProfiles', 'data'],
],
'profilesWithAvatarUrl'
)
),
flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'profilesWithAvatarUrl'],
['context', 'data', 'paginatedProfiles', 'metadata'],
],
'normalizedBody'
)
),
lift(
withLogging(
orThrow(
[
[ProfileDbBlockError, 500],
[AvatarBlockError, 500],
],
[['context', 'data', 'normalizedBody']]
)
)
)
),
method: 'GET',
path: '/profiles/identities/:identityId',
validators: [
isAuその後ticated(),
isSelf(['params', 'requestParams', 'identityId']),
],
});

createProfileFollowRoute

実装

エンドポイント: PUT /profiles/:profileId/profile-follows/:followProfileId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。参照: createProfileFollowSchema.

パイプライン: 両方の Profile を読み込み、その後 createProfileFollow.

成功: 204

失敗: 404 Profile、409 重複、500 データベース失敗。

完全なソースを表示
export const createProfileFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'followProfileId'],
],
'followProfile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createProfileFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followProfileId'],
],
'createProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
[ProfileAlreadyFollowedBlockError, 409],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/profile-follows/:followProfileId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProfileFollowRoute

実装

エンドポイント: DELETE /profiles/:profileId/profile-follows/:followProfileId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。

パイプライン: 両方の Profile を読み込み、その後 deleteProfileFollow.

成功: 204

失敗: 404 Profile/フォロー、500 データベース失敗。

完全なソースを表示
export const deleteProfileFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'followProfileId'],
],
'followProfile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteProfileFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followProfileId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileFollowNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/profile-follows/:followProfileId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

getProfileFollowersRoute

実装

エンドポイント: GET /profiles/:profileId/followers

アクセス: 認証済み管理者または所有者。

リクエスト: パス ID とページネーション。

パイプライン: Profile を検証し、フォロワーフィルターを構築してページネーションする findProfiles, その後 normalizeFollowers.

成功: 200{ data, metadata: { pagination } }

失敗: ProfileNotFoundBlockError404ProfileUnexpectedDBError500 に対応付けられます。未対応のブロックエラーは伝播します。

完全なソースを表示
export const getProfileFollowersRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
buildProfileFollowersByFollowProfileIdQuery,
[['params', 'requestParams', 'profileId']],
'filters'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(buildWithoutMongoIdFindOptions, [], 'options')
)
),

flatMapAsync(
withPagination(
withLogging(
applyPayloadArgs(
findProfiles,
[
['context', 'db', 'profiles'],
['context', 'data', 'filters'],
['context', 'data', 'options'],
],
'paginatedFollowers'
)
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeFollowers,
[
['context', 'fileStorageDriver'],
['context', 'data', 'paginatedFollowers', 'data'],
],
'normalizedFollowers'
)
)
),

flatMapAsync(
applyPayloadArgs(
applySpec({ data: nthArg(0), metadata: { pagination: nthArg(1) } }),
[
['context', 'data', 'normalizedFollowers'],
['context', 'data', 'paginatedFollowers', 'metadata'],
],
'normalizedBody'
)
),

lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'normalizedBody'], 200]
)
)
),
method: 'GET',
path: '/profiles/:profileId/followers',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

createOrganizationFollowRoute

実装

エンドポイント: PUT /profiles/:profileId/organization-follows/:followOrganizationId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。

パイプライン: verifies profile and organization, その後 createOrganizationFollow.

成功: 204

失敗: 404、重複 409、データベース 500

完全なソースを表示
export const createOrganizationFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getOrganizationById,
[
['context', 'db', 'organizations'],
['params', 'requestParams', 'followOrganizationId'],
],
'followOrganization'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createOrganizationFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followOrganizationId'],
],
'createOrganizationFollowResult'
)
)
),

lift(
orThrow(
[
[OrganizationNotFoundError, 404],
[ProfileNotFoundBlockError, 404],
[OrganizationAlreadyFollowedBlockError, 409],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/organization-follows/:followOrganizationId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteOrganizationFollowRoute

実装

エンドポイント: DELETE /profiles/:profileId/organization-follows/:followOrganizationId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。

パイプライン: verifies profile and organization, その後 deleteOrganizationFollow.

成功: 204

失敗: 404 またはデータベース 500

完全なソースを表示
export const deleteOrganizationFollowRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getOrganizationById,
[
['context', 'db', 'organizations'],
['params', 'requestParams', 'followOrganizationId'],
],
'organization'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteOrganizationFollow,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'followOrganizationId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[OrganizationFollowNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/organization-follows/:followOrganizationId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

createProductLikeRoute

実装

エンドポイント: PUT /profiles/:profileId/product-likes/:likeProductId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。

パイプライン: verifies profile and product, その後 createProductLike.

成功: 204

失敗: 404、重複 409、データベース 500

完全なソースを表示
export const createProductLikeRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProductById,
[
['context', 'db', 'products'],
['params', 'requestParams', 'likeProductId'],
],
'likeProduct'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
createProductLike,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'likeProductId'],
],
'createProductLikeResult'
)
)
),
lift(
orThrow(
[
[ProductNotFoundBlockError, 404],
[ProfileNotFoundBlockError, 404],
[ProductAlreadyLikedBlockError, 409],
[ProfileUnexpectedDBError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'PUT',
path: '/profiles/:profileId/product-likes/:likeProductId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});

deleteProductLikeRoute

実装

エンドポイント: DELETE /profiles/:profileId/product-likes/:likeProductId

アクセス: 認証済み管理者または所有者。

リクエスト: 両方のパス ID。

パイプライン: verifies profile and product, その後 deleteProductLike.

成功: 204

失敗: 404 またはデータベース 500

完全なソースを表示
export const deleteProductLikeRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getProfileById,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
],
'profile'
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
getProductById,
[
['context', 'db', 'products'],
['params', 'requestParams', 'likeProductId'],
],
'profile'
)
)
),

flatMapAsync(
withLogging(
applyPayloadArgs(
deleteProductLike,
[
['context', 'db', 'profiles'],
['params', 'requestParams', 'profileId'],
['params', 'requestParams', 'likeProductId'],
],
'deleteProfileFollowResult'
)
)
),
lift(
orThrow(
[
[ProfileNotFoundBlockError, 404],
[ProductLikeNotFoundBlockError, 404],
[ProductNotFoundBlockError, 404],
[ProfileUnexpectedDBError, 500],
[ProductNotFoundBlockError, 500],
],
[['context', 'data', 'undefined'], 204]
)
)
),
method: 'DELETE',
path: '/profiles/:profileId/product-likes/:likeProductId',
validators: [
isAuその後ticated(),
some(
checkIdentityType(['admin']),
ownsProfile(['params', 'requestParams', 'profileId'])
),
],
});