feat(authentication, types): Create authentication entities and services (#5979)

* create authentication entities

* ensure hashes are not returned

* re-add integration test argument

* update filterableprops

* update type

* pr feedback

* update serialization

* move finding existing entities and validation to service layer

* update types

* update models

* update user model

* pr feedback

* fix build

* pr feedback

* update integration test fixtures

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Philip Korsholm
2024-01-08 09:50:12 +01:00
committed by GitHub
co-authored by Oli Juhl
parent 6b0b3fed7a
commit 479a8b82a9
36 changed files with 2394 additions and 32 deletions
@@ -0,0 +1,139 @@
import { Context, DAL, FindConfig } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
retrieveEntity,
} from "@medusajs/utils"
import { AuthProvider } from "@models"
import { AuthProviderRepository } from "@repositories"
import { RepositoryTypes, ServiceTypes } from "@types"
type InjectedDependencies = {
authProviderRepository: DAL.RepositoryService
}
export default class AuthProviderService<
TEntity extends AuthProvider = AuthProvider
> {
protected readonly authProviderRepository_: DAL.RepositoryService
constructor({ authProviderRepository }: InjectedDependencies) {
this.authProviderRepository_ = authProviderRepository
}
@InjectManager("authProviderRepository_")
async retrieve(
provider: string,
config: FindConfig<ServiceTypes.AuthProviderDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<AuthProvider, ServiceTypes.AuthProviderDTO>({
id: provider,
identifierColumn: "provider",
entityName: AuthProvider.name,
repository: this.authProviderRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("authProviderRepository_")
async list(
filters: ServiceTypes.FilterableAuthProviderProps = {},
config: FindConfig<ServiceTypes.AuthProviderDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryConfig = ModulesSdkUtils.buildQuery<AuthProvider>(
filters,
config
)
return (await this.authProviderRepository_.find(
queryConfig,
sharedContext
)) as TEntity[]
}
@InjectManager("authProviderRepository_")
async listAndCount(
filters: ServiceTypes.FilterableAuthProviderProps = {},
config: FindConfig<ServiceTypes.AuthProviderDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryConfig = ModulesSdkUtils.buildQuery<AuthProvider>(
filters,
config
)
return (await this.authProviderRepository_.findAndCount(
queryConfig,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager("authProviderRepository_")
async create(
data: ServiceTypes.CreateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (
this.authProviderRepository_ as AuthProviderRepository
).create(data, sharedContext)) as TEntity[]
}
@InjectTransactionManager("authProviderRepository_")
async update(
data: ServiceTypes.UpdateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const authProviderIds = data.map(
(authProviderData) => authProviderData.provider
)
const existingAuthProviders = await this.list(
{
provider: authProviderIds,
},
{},
sharedContext
)
const updates: RepositoryTypes.UpdateAuthProviderDTO[] = []
const existingAuthProvidersMap = new Map(
existingAuthProviders.map<[string, AuthProvider]>((authProvider) => [
authProvider.provider,
authProvider,
])
)
for (const update of data) {
const provider = existingAuthProvidersMap.get(update.provider)
if (!provider) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`AuthProvider with provider "${update.provider}" not found`
)
}
updates.push({ update, provider })
}
return (await (
this.authProviderRepository_ as AuthProviderRepository
).update(updates, sharedContext)) as TEntity[]
}
@InjectTransactionManager("authProviderRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authProviderRepository_.delete(ids, sharedContext)
}
}
@@ -0,0 +1,126 @@
import { Context, DAL, FindConfig } from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
retrieveEntity,
} from "@medusajs/utils"
import { AuthUser } from "@models"
import { AuthUserRepository } from "@repositories"
import { RepositoryTypes, ServiceTypes } from "@types"
type InjectedDependencies = {
authUserRepository: DAL.RepositoryService
}
export default class AuthUserService<TEntity extends AuthUser = AuthUser> {
protected readonly authUserRepository_: DAL.RepositoryService
constructor({ authUserRepository }: InjectedDependencies) {
this.authUserRepository_ = authUserRepository
}
@InjectManager("authUserRepository_")
async retrieve(
provider: string,
config: FindConfig<ServiceTypes.AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<AuthUser, ServiceTypes.AuthUserDTO>({
id: provider,
entityName: AuthUser.name,
repository: this.authUserRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("authUserRepository_")
async list(
filters: ServiceTypes.FilterableAuthProviderProps = {},
config: FindConfig<ServiceTypes.AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryConfig = ModulesSdkUtils.buildQuery<AuthUser>(filters, config)
return (await this.authUserRepository_.find(
queryConfig,
sharedContext
)) as TEntity[]
}
@InjectManager("authUserRepository_")
async listAndCount(
filters: ServiceTypes.FilterableAuthUserProps = {},
config: FindConfig<ServiceTypes.AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryConfig = ModulesSdkUtils.buildQuery<AuthUser>(filters, config)
return (await this.authUserRepository_.findAndCount(
queryConfig,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager("authUserRepository_")
async create(
data: ServiceTypes.CreateAuthUserDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (this.authUserRepository_ as AuthUserRepository).create(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("authUserRepository_")
async update(
data: ServiceTypes.UpdateAuthUserDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const existingUsers = await this.list(
{ id: data.map(({ id }) => id) },
{},
sharedContext
)
const existingUsersMap = new Map(
existingUsers.map<[string, AuthUser]>((authUser) => [
authUser.id,
authUser,
])
)
const updates: RepositoryTypes.UpdateAuthUserDTO[] = []
for (const update of data) {
const user = existingUsersMap.get(update.id)
if (!user) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`AuthUser with id "${update.id}" not found`
)
}
updates.push({ update, user })
}
return (await (this.authUserRepository_ as AuthUserRepository).update(
updates,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("authUserRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authUserRepository_.delete(ids, sharedContext)
}
}
@@ -1,29 +1,339 @@
import {
AuthenticationTypes,
Context,
DAL,
FindConfig,
InternalModuleDeclaration,
ModuleJoinerConfig,
} from "@medusajs/types"
import { AuthUser } from "@models"
import { AuthProvider, AuthUser } from "@models"
import { joinerConfig } from "../joiner-config"
import { AuthProviderService, AuthUserService } from "@services"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
} from "@medusajs/utils"
import {
AuthProviderDTO,
AuthUserDTO,
CreateAuthProviderDTO,
CreateAuthUserDTO,
FilterableAuthProviderProps,
FilterableAuthUserProps,
UpdateAuthUserDTO,
} from "@medusajs/types/dist/authentication/common"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
authUserService: AuthUserService<any>
authProviderService: AuthProviderService<any>
}
export default class AuthenticationModuleService<
TAuthUser extends AuthUser = AuthUser
TAuthUser extends AuthUser = AuthUser,
TAuthProvider extends AuthProvider = AuthProvider
> implements AuthenticationTypes.IAuthenticationModuleService
{
protected baseRepository_: DAL.RepositoryService
protected authUserService_: AuthUserService<TAuthUser>
protected authProviderService_: AuthProviderService<TAuthProvider>
constructor(
{ baseRepository }: InjectedDependencies,
{
authUserService,
authProviderService,
baseRepository,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
this.baseRepository_ = baseRepository
this.authUserService_ = authUserService
this.authProviderService_ = authProviderService
}
async retrieveAuthProvider(
provider: string,
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
): Promise<AuthProviderDTO> {
const authProvider = await this.authProviderService_.retrieve(
provider,
config,
sharedContext
)
return await this.baseRepository_.serialize<AuthenticationTypes.AuthProviderDTO>(
authProvider,
{ populate: true }
)
}
async listAuthProviders(
filters: FilterableAuthProviderProps = {},
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
): Promise<AuthProviderDTO[]> {
const authProviders = await this.authProviderService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<
AuthenticationTypes.AuthProviderDTO[]
>(authProviders, { populate: true })
}
@InjectManager("baseRepository_")
async listAndCountAuthProviders(
filters: FilterableAuthProviderProps = {},
config: FindConfig<AuthProviderDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<[AuthenticationTypes.AuthProviderDTO[], number]> {
const [authProviders, count] = await this.authProviderService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<
AuthenticationTypes.AuthProviderDTO[]
>(authProviders, { populate: true }),
count,
]
}
async createAuthProvider(
data: CreateAuthProviderDTO[],
sharedContext?: Context
): Promise<AuthProviderDTO[]>
async createAuthProvider(
data: CreateAuthProviderDTO,
sharedContext?: Context
): Promise<AuthProviderDTO>
@InjectManager("baseRepository_")
async createAuthProvider(
data: CreateAuthProviderDTO | CreateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthenticationTypes.AuthProviderDTO | AuthenticationTypes.AuthProviderDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const providers = await this.createAuthProviders_(input, sharedContext)
const serializedProviders = await this.baseRepository_.serialize<
AuthenticationTypes.AuthProviderDTO[]
>(providers, {
populate: true,
})
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
}
@InjectTransactionManager("baseRepository_")
protected async createAuthProviders_(
data: any[],
@MedusaContext() sharedContext: Context
): Promise<AuthenticationTypes.AuthProviderDTO[]> {
return await this.authProviderService_.create(data, sharedContext)
}
updateAuthProvider(
data: AuthenticationTypes.UpdateAuthProviderDTO[],
sharedContext?: Context
): Promise<AuthProviderDTO[]>
updateAuthProvider(
data: AuthenticationTypes.UpdateAuthProviderDTO,
sharedContext?: Context
): Promise<AuthProviderDTO>
@InjectManager("baseRepository_")
async updateAuthProvider(
data:
| AuthenticationTypes.UpdateAuthProviderDTO[]
| AuthenticationTypes.UpdateAuthProviderDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthenticationTypes.AuthProviderDTO | AuthenticationTypes.AuthProviderDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const providers = await this.updateAuthProvider_(input, sharedContext)
const serializedProviders = await this.baseRepository_.serialize<
AuthenticationTypes.AuthProviderDTO[]
>(providers, {
populate: true,
})
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
}
async updateAuthProvider_(
data: AuthenticationTypes.UpdateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<AuthProviderDTO[]> {
return await this.authProviderService_.update(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async deleteAuthProvider(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authProviderService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveAuthUser(
id: string,
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<AuthUserDTO> {
const authUser = await this.authUserService_.retrieve(
id,
config,
sharedContext
)
return await this.baseRepository_.serialize<AuthenticationTypes.AuthUserDTO>(
authUser,
{
exclude: ["password_hash"],
}
)
}
@InjectManager("baseRepository_")
async listAuthUsers(
filters: FilterableAuthUserProps = {},
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<AuthUserDTO[]> {
const authUsers = await this.authUserService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<
AuthenticationTypes.AuthUserDTO[]
>(authUsers, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listAndCountAuthUsers(
filters: FilterableAuthUserProps = {},
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[AuthUserDTO[], number]> {
const [authUsers, count] = await this.authUserService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<AuthenticationTypes.AuthUserDTO[]>(
authUsers,
{
populate: true,
}
),
count,
]
}
createAuthUser(
data: CreateAuthUserDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
createAuthUser(
data: CreateAuthUserDTO,
sharedContext?: Context
): Promise<AuthUserDTO>
@InjectManager("baseRepository_")
async createAuthUser(
data: CreateAuthUserDTO[] | CreateAuthUserDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthenticationTypes.AuthUserDTO | AuthenticationTypes.AuthUserDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const authUsers = await this.createAuthUsers_(input, sharedContext)
const serializedUsers = await this.baseRepository_.serialize<
AuthenticationTypes.AuthUserDTO[]
>(authUsers, {
populate: true,
})
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
}
@InjectTransactionManager("baseRepository_")
protected async createAuthUsers_(
data: CreateAuthUserDTO[],
@MedusaContext() sharedContext: Context
): Promise<TAuthUser[]> {
return await this.authUserService_.create(data, sharedContext)
}
updateAuthUser(
data: UpdateAuthUserDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
updateAuthUser(
data: UpdateAuthUserDTO,
sharedContext?: Context
): Promise<AuthUserDTO>
@InjectManager("baseRepository_")
async updateAuthUser(
data: UpdateAuthUserDTO | UpdateAuthUserDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthenticationTypes.AuthUserDTO | AuthenticationTypes.AuthUserDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const updatedUsers = await this.updateAuthUsers_(input, sharedContext)
const serializedUsers = await this.baseRepository_.serialize<
AuthenticationTypes.AuthUserDTO[]
>(updatedUsers, {
populate: true,
})
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
}
@InjectTransactionManager("baseRepository_")
protected async updateAuthUsers_(
data: UpdateAuthUserDTO[],
@MedusaContext() sharedContext: Context
): Promise<TAuthUser[]> {
return await this.authUserService_.update(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async deleteAuthUser(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authUserService_.delete(ids, sharedContext)
}
__joinerConfig(): ModuleJoinerConfig {
@@ -1 +1,3 @@
export { default as AuthenticationModuleService } from "./authentication-module"
export { default as AuthProviderService } from "./auth-provider"
export { default as AuthUserService } from "./auth-user"