-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(user): Migrate user module to DML #10389
Changes from 1 commit
2de84d0
c6e75b2
b12f7ed
6479f0e
6212ab3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,22 +8,16 @@ import jwt, { JwtPayload } from "jsonwebtoken" | |
|
||
jest.setTimeout(30000) | ||
|
||
const expireDate = new Date().setMilliseconds( | ||
new Date().getMilliseconds() + 60 * 60 * 24 | ||
) | ||
|
||
const defaultInviteData = [ | ||
{ | ||
id: "1", | ||
email: "[email protected]", | ||
token: "test", | ||
expires_at: expireDate, | ||
}, | ||
{ | ||
id: "2", | ||
email: "[email protected]", | ||
token: "test", | ||
expires_at: expireDate, | ||
}, | ||
] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
export { default as User } from "./user" | ||
export { default as Invite } from "./invite" | ||
export { User } from "./user" | ||
export { Invite } from "./invite" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,112 +1,24 @@ | ||
import { | ||
BeforeCreate, | ||
Entity, | ||
Filter, | ||
Index, | ||
OnInit, | ||
OptionalProps, | ||
PrimaryKey, | ||
Property, | ||
} from "@mikro-orm/core" | ||
|
||
import { DAL } from "@medusajs/framework/types" | ||
import { | ||
DALUtils, | ||
createPsqlIndexStatementHelper, | ||
generateEntityId, | ||
Searchable, | ||
} from "@medusajs/framework/utils" | ||
|
||
const inviteEmailIndexName = "IDX_invite_email" | ||
const inviteEmailIndexStatement = createPsqlIndexStatementHelper({ | ||
name: inviteEmailIndexName, | ||
tableName: "invite", | ||
columns: "email", | ||
where: "deleted_at IS NULL", | ||
unique: true, | ||
}).expression | ||
|
||
const inviteTokenIndexName = "IDX_invite_token" | ||
const inviteTokenIndexStatement = createPsqlIndexStatementHelper({ | ||
name: inviteTokenIndexName, | ||
tableName: "invite", | ||
columns: "token", | ||
where: "deleted_at IS NULL", | ||
}).expression | ||
|
||
const inviteDeletedAtIndexName = "IDX_invite_deleted_at" | ||
const inviteDeletedAtIndexStatement = createPsqlIndexStatementHelper({ | ||
name: inviteDeletedAtIndexName, | ||
tableName: "invite", | ||
columns: "deleted_at", | ||
where: "deleted_at IS NOT NULL", | ||
}).expression | ||
|
||
type OptionalFields = | ||
| "metadata" | ||
| "accepted" | ||
| DAL.SoftDeletableModelDateColumns | ||
@Entity({ tableName: "invite" }) | ||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) | ||
export default class Invite { | ||
[OptionalProps]: OptionalFields | ||
|
||
@PrimaryKey({ columnType: "text" }) | ||
id: string | ||
|
||
@Index({ | ||
name: inviteEmailIndexName, | ||
expression: inviteEmailIndexStatement, | ||
}) | ||
@Searchable() | ||
@Property({ columnType: "text" }) | ||
email: string | ||
|
||
@Property({ columnType: "boolean" }) | ||
accepted: boolean = false | ||
|
||
@Index({ | ||
name: inviteTokenIndexName, | ||
expression: inviteTokenIndexStatement, | ||
import { model } from "@medusajs/framework/utils" | ||
|
||
export const Invite = model | ||
.define("invite", { | ||
id: model.id({ prefix: "invite" }).primaryKey(), | ||
email: model.text().searchable(), | ||
accepted: model.boolean().default(false), | ||
token: model.text(), | ||
expires_at: model.dateTime(), | ||
metadata: model.json().nullable(), | ||
}) | ||
@Property({ columnType: "text" }) | ||
token: string | ||
|
||
@Property({ columnType: "timestamptz" }) | ||
expires_at: Date | ||
|
||
@Property({ columnType: "jsonb", nullable: true }) | ||
metadata: Record<string, unknown> | null = null | ||
|
||
@Property({ | ||
onCreate: () => new Date(), | ||
columnType: "timestamptz", | ||
defaultRaw: "now()", | ||
}) | ||
created_at: Date | ||
|
||
@Property({ | ||
onCreate: () => new Date(), | ||
onUpdate: () => new Date(), | ||
columnType: "timestamptz", | ||
defaultRaw: "now()", | ||
}) | ||
updated_at: Date | ||
|
||
@Index({ | ||
name: inviteDeletedAtIndexName, | ||
expression: inviteDeletedAtIndexStatement, | ||
}) | ||
@Property({ columnType: "timestamptz", nullable: true }) | ||
deleted_at: Date | null = null | ||
|
||
@OnInit() | ||
onInit() { | ||
this.id = generateEntityId(this.id, "invite") | ||
} | ||
|
||
@BeforeCreate() | ||
beforeCreate() { | ||
this.id = generateEntityId(this.id, "invite") | ||
} | ||
} | ||
.indexes([ | ||
{ | ||
name: "IDX_invite_email", | ||
on: ["email"], | ||
unique: true, | ||
where: "deleted_at IS NULL", | ||
}, | ||
{ | ||
name: "IDX_invite_token", | ||
on: ["token"], | ||
where: "deleted_at IS NULL", | ||
}, | ||
]) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,19 @@ | ||
import { | ||
BeforeCreate, | ||
Entity, | ||
Filter, | ||
Index, | ||
OnInit, | ||
OptionalProps, | ||
PrimaryKey, | ||
Property, | ||
} from "@mikro-orm/core" | ||
|
||
import { DAL } from "@medusajs/framework/types" | ||
import { | ||
createPsqlIndexStatementHelper, | ||
DALUtils, | ||
generateEntityId, | ||
Searchable, | ||
} from "@medusajs/framework/utils" | ||
|
||
const userEmailIndexName = "IDX_user_email" | ||
const userEmailIndexStatement = createPsqlIndexStatementHelper({ | ||
name: userEmailIndexName, | ||
unique: true, | ||
tableName: "user", | ||
columns: "email", | ||
where: "deleted_at IS NULL", | ||
}) | ||
|
||
const userDeletedAtIndexName = "IDX_user_deleted_at" | ||
const userDeletedAtIndexStatement = createPsqlIndexStatementHelper({ | ||
name: userDeletedAtIndexName, | ||
tableName: "user", | ||
columns: "deleted_at", | ||
where: "deleted_at IS NOT NULL", | ||
}).expression | ||
|
||
type OptionalFields = | ||
| "first_name" | ||
| "last_name" | ||
| "metadata" | ||
| "avatar_url" | ||
| DAL.SoftDeletableModelDateColumns | ||
|
||
@Entity() | ||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) | ||
export default class User { | ||
[OptionalProps]?: OptionalFields | ||
|
||
@PrimaryKey({ columnType: "text" }) | ||
id!: string | ||
|
||
@Searchable() | ||
@Property({ columnType: "text", nullable: true }) | ||
first_name: string | null = null | ||
|
||
@Searchable() | ||
@Property({ columnType: "text", nullable: true }) | ||
last_name: string | null = null | ||
|
||
@userEmailIndexStatement.MikroORMIndex() | ||
@Searchable() | ||
@Property({ columnType: "text" }) | ||
email: string | ||
|
||
@Property({ columnType: "text", nullable: true }) | ||
avatar_url: string | null = null | ||
|
||
@Property({ columnType: "jsonb", nullable: true }) | ||
metadata: Record<string, unknown> | null = null | ||
|
||
@Property({ | ||
onCreate: () => new Date(), | ||
columnType: "timestamptz", | ||
defaultRaw: "now()", | ||
}) | ||
created_at: Date | ||
|
||
@Property({ | ||
onCreate: () => new Date(), | ||
onUpdate: () => new Date(), | ||
columnType: "timestamptz", | ||
defaultRaw: "now()", | ||
import { model } from "@medusajs/framework/utils" | ||
|
||
export const User = model | ||
.define("user", { | ||
id: model.id({ prefix: "user" }).primaryKey(), | ||
first_name: model.text().searchable().nullable(), | ||
last_name: model.text().searchable().nullable(), | ||
email: model.text().searchable().searchable(), | ||
avatar_url: model.text().nullable(), | ||
adrien2p marked this conversation as resolved.
Show resolved
Hide resolved
|
||
metadata: model.json().nullable(), | ||
}) | ||
updated_at: Date | ||
|
||
@Index({ | ||
name: userDeletedAtIndexName, | ||
expression: userDeletedAtIndexStatement, | ||
}) | ||
@Property({ columnType: "timestamptz", nullable: true }) | ||
deleted_at?: Date | null = null | ||
|
||
@BeforeCreate() | ||
onCreate() { | ||
this.id = generateEntityId(this.id, "user") | ||
} | ||
|
||
@OnInit() | ||
onInit() { | ||
this.id = generateEntityId(this.id, "user") | ||
} | ||
} | ||
.indexes([ | ||
{ | ||
name: "IDX_user_email", | ||
unique: true, | ||
on: ["email"], | ||
where: "deleted_at IS NULL", | ||
}, | ||
]) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { | ||
Context, | ||
DAL, | ||
InferEntityType, | ||
InternalModuleDeclaration, | ||
ModulesSdkTypes, | ||
UserTypes, | ||
|
@@ -9,6 +10,7 @@ import { | |
arrayDifference, | ||
CommonEvents, | ||
EmitEvents, | ||
generateEntityId, | ||
InjectManager, | ||
InjectTransactionManager, | ||
MedusaContext, | ||
|
@@ -41,8 +43,12 @@ export default class UserModuleService | |
{ | ||
protected baseRepository_: DAL.RepositoryService | ||
|
||
protected readonly userService_: ModulesSdkTypes.IMedusaInternalService<User> | ||
protected readonly inviteService_: ModulesSdkTypes.IMedusaInternalService<Invite> | ||
protected readonly userService_: ModulesSdkTypes.IMedusaInternalService< | ||
InferEntityType<typeof User> | ||
> | ||
protected readonly inviteService_: ModulesSdkTypes.IMedusaInternalService< | ||
InferEntityType<typeof Invite> | ||
> | ||
protected readonly config: { jwtSecret: string; expiresIn: number } | ||
|
||
constructor( | ||
|
@@ -151,9 +157,7 @@ export default class UserModuleService | |
const updates = invites.map((invite) => { | ||
return { | ||
id: invite.id, | ||
expires_at: new Date().setMilliseconds( | ||
new Date().getMilliseconds() + this.config.expiresIn * 1000 | ||
), | ||
expires_at: new Date(Date.now() + this.config.expiresIn * 1000), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NOTE: this was giving a big int instead of a date, now it does the same thing but provide a date instead |
||
token: this.generateToken({ id: invite.id, email: invite.email }), | ||
} | ||
}) | ||
|
@@ -296,7 +300,7 @@ export default class UserModuleService | |
private async createInvites_( | ||
data: UserTypes.CreateInviteDTO[], | ||
@MedusaContext() sharedContext: Context = {} | ||
): Promise<Invite[]> { | ||
): Promise<InferEntityType<typeof Invite>[]> { | ||
const alreadyExistingUsers = await this.listUsers({ | ||
email: data.map((d) => d.email), | ||
}) | ||
|
@@ -311,26 +315,16 @@ export default class UserModuleService | |
} | ||
|
||
const toCreate = data.map((invite) => { | ||
const id = generateEntityId((invite as { id?: string }).id, "invite") | ||
adrien2p marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return { | ||
...invite, | ||
expires_at: new Date(), | ||
token: "placeholder", | ||
id, | ||
expires_at: new Date(Date.now() + this.config.expiresIn * 1000), | ||
token: this.generateToken({ id, email: invite.email }), | ||
} | ||
}) | ||
|
||
const created = await this.inviteService_.create(toCreate, sharedContext) | ||
|
||
const updates = created.map((invite) => { | ||
return { | ||
id: invite.id, | ||
expires_at: new Date().setMilliseconds( | ||
new Date().getMilliseconds() + this.config.expiresIn * 1000 | ||
), | ||
token: this.generateToken({ id: invite.id, email: invite.email }), | ||
} | ||
}) | ||
|
||
return await this.inviteService_.update(updates, sharedContext) | ||
return await this.inviteService_.create(toCreate, sharedContext) | ||
} | ||
Comment on lines
+318
to
328
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NOTE: This is a simplification, instead of doing a create and then an update, we only perform a create but the result remain unchanged |
||
|
||
// @ts-ignore | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE: The given value was overritten by the module so it was not doing anything