Skip to content

Commit 86ec9e5

Browse files
committed
feat(models): Repository
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent 86ae3ec commit 86ec9e5

15 files changed

+778
-30
lines changed

.dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dessant
1414
dohm
1515
dotenv
1616
dtos
17+
embeddable
1718
fbca
1819
gcloud
1920
ggshield

__fixtures__/.gitkeep

Whitespace-only changes.

__fixtures__/account.entity.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* @file Fixtures - Account
3+
* @module fixtures/Account
4+
*/
5+
6+
import { IsNullable, IsOptional, IsTimestamp } from '#src/decorators'
7+
import type { IEntity } from '#src/interfaces'
8+
import { isNIL, type Nullable } from '@flex-development/tutils'
9+
import {
10+
Entity,
11+
PrimaryKey,
12+
Property,
13+
SerializedPrimaryKey,
14+
Unique,
15+
type EntityData
16+
} from '@mikro-orm/core'
17+
import { ObjectId } from '@mikro-orm/mongodb'
18+
import { IsEmail, IsInstance, IsLowercase, IsMongoId } from 'class-validator'
19+
import plur from 'plur'
20+
21+
/**
22+
* Data used to create a new account via the entity constructor.
23+
*/
24+
type AccountDTO = Omit<EntityData<Account>, 'email'> & { email: string }
25+
26+
/**
27+
* Data model representing an entity in the accounts collection.
28+
*
29+
* @class
30+
* @implements {IEntity}
31+
*/
32+
@Entity({ collection: plur(Account.name).toLowerCase() })
33+
class Account implements IEntity {
34+
/**
35+
* BSON object id.
36+
*
37+
* @public
38+
* @readonly
39+
* @member {ObjectId} _id
40+
*/
41+
@PrimaryKey<Account>({ type: ObjectId })
42+
@IsInstance(ObjectId)
43+
@IsOptional()
44+
public _id!: ObjectId
45+
46+
/**
47+
* Unix timestamp indicating when account was created.
48+
*
49+
* @public
50+
* @readonly
51+
* @member {number} created_at
52+
*/
53+
@Property<Account>({ type: 'integer' })
54+
@IsTimestamp()
55+
public created_at: number = Date.now()
56+
57+
/**
58+
* Account email address.
59+
*
60+
* @public
61+
* @readonly
62+
* @member {Lowercase<string>} email
63+
*/
64+
@Property<Account>({ type: 'string' })
65+
@Unique<Account>({ name: 'email' })
66+
@IsEmail()
67+
@IsLowercase()
68+
public email!: Lowercase<string>
69+
70+
/**
71+
* Unique identifier for account.
72+
*
73+
* @public
74+
* @readonly
75+
* @member {string} id
76+
*/
77+
@SerializedPrimaryKey<Account>({ type: 'string' })
78+
@IsMongoId()
79+
@IsOptional()
80+
public id!: string
81+
82+
/**
83+
* Unix timestamp indicating when account was last modified.
84+
*
85+
* @public
86+
* @readonly
87+
* @member {Nullable<number>} updated_at
88+
*/
89+
@Property<Account>({ nullable: true, type: 'integer' })
90+
@IsTimestamp()
91+
@IsNullable()
92+
public updated_at: Nullable<number> = null
93+
94+
/**
95+
* Creates a new account entity.
96+
*
97+
* @param {AccountDTO} dto - Data transfer object
98+
* @param {string} dto.email - Account email
99+
*/
100+
constructor({ _id, created_at, email, updated_at }: AccountDTO) {
101+
if (!isNIL(_id)) {
102+
this._id = _id
103+
this.id = this._id.toString()
104+
}
105+
106+
!isNIL(created_at) && (this.created_at = created_at)
107+
!isNIL(updated_at) && (this.updated_at = updated_at)
108+
109+
this.email = email.trim().toLowerCase()
110+
}
111+
}
112+
113+
export default Account

__fixtures__/account.factory.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @file Fixtures - AccountFactory
3+
* @module fixtures/AccountFactory
4+
*/
5+
6+
import type { Constructor } from '@mikro-orm/core'
7+
import type { MongoEntityManager } from '@mikro-orm/mongodb'
8+
import { Factory, Faker } from '@mikro-orm/seeder'
9+
import Account from './account.entity'
10+
11+
/**
12+
* Account entity factory.
13+
*
14+
* @see {@linkcode Account}
15+
*
16+
* @class
17+
* @extends {Factory<Account>}
18+
*/
19+
class AccountFactory extends Factory<Account> {
20+
/**
21+
* Entity the factory generates entity instances for.
22+
*
23+
* @public
24+
* @readonly
25+
* @member {Constructor<Account>} model
26+
*/
27+
public readonly model: Constructor<Account>
28+
29+
/**
30+
* Creates a new {@linkcode Account} entity factory.
31+
*
32+
* @param {MongoEntityManager} em - Entity manager
33+
*/
34+
constructor(em: MongoEntityManager) {
35+
super(em)
36+
this.model = Account
37+
}
38+
39+
/**
40+
* Returns the default set of attribute values that should be applied when
41+
* creating an {@linkcode Account} entity.
42+
*
43+
* @see https://fakerjs.dev
44+
*
45+
* @protected
46+
* @override
47+
*
48+
* @param {Faker} faker - Faker library
49+
* @return {{ email: Account['email'] }} Account entity data
50+
*/
51+
protected override definition(faker: Faker): { email: Account['email'] } {
52+
return { email: faker.internet.email().toLowerCase() }
53+
}
54+
}
55+
56+
export default AccountFactory

__fixtures__/subscriber.entity.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @file Fixtures - Subscriber
3+
* @module fixtures/Subscriber
4+
*/
5+
6+
import { IsNullable, IsOptional, IsTimestamp } from '#src/decorators'
7+
import type { IEntity } from '#src/interfaces'
8+
import { isNIL, type Nullable } from '@flex-development/tutils'
9+
import {
10+
Entity,
11+
Index,
12+
PrimaryKey,
13+
Property,
14+
SerializedPrimaryKey,
15+
Unique,
16+
type EntityData
17+
} from '@mikro-orm/core'
18+
import { ObjectId } from '@mikro-orm/mongodb'
19+
import {
20+
IsEmail,
21+
IsInstance,
22+
IsLowercase,
23+
IsMongoId,
24+
IsString,
25+
MinLength
26+
} from 'class-validator'
27+
import plur from 'plur'
28+
29+
/**
30+
* Data used to create a new subscriber via the entity constructor.
31+
*/
32+
type SubscriberDTO = Omit<EntityData<Subscriber>, 'email' | 'name'> & {
33+
email: string
34+
name: string
35+
}
36+
37+
/**
38+
* Data model representing an entity in the subscribers collection.
39+
*
40+
* @class
41+
* @implements {IEntity}
42+
*/
43+
@Entity({ collection: plur(Subscriber.name).toLowerCase() })
44+
class Subscriber implements IEntity {
45+
/**
46+
* BSON object id.
47+
*
48+
* @public
49+
* @readonly
50+
* @member {ObjectId} _id
51+
*/
52+
@PrimaryKey<Subscriber>({ type: ObjectId })
53+
@IsInstance(ObjectId)
54+
@IsOptional()
55+
public _id!: ObjectId
56+
57+
/**
58+
* Unix timestamp indicating when subscriber signed up.
59+
*
60+
* @public
61+
* @readonly
62+
* @member {number} created_at
63+
*/
64+
@Property<Subscriber>({ type: 'integer' })
65+
@IsTimestamp()
66+
public created_at: number = Date.now()
67+
68+
/**
69+
* Subscriber email address.
70+
*
71+
* @public
72+
* @readonly
73+
* @member {Lowercase<string>} email
74+
*/
75+
@Property<Subscriber>({ type: 'string' })
76+
@Unique<Subscriber>({ name: 'email' })
77+
@IsEmail()
78+
@IsLowercase()
79+
public email!: Lowercase<string>
80+
81+
/**
82+
* Unique identifier for subscriber.
83+
*
84+
* @public
85+
* @readonly
86+
* @member {string} id
87+
*/
88+
@SerializedPrimaryKey<Subscriber>({ type: 'string' })
89+
@IsMongoId()
90+
@IsOptional()
91+
public id!: string
92+
93+
/**
94+
* Subscriber name.
95+
*
96+
* @public
97+
* @readonly
98+
* @member {string} name
99+
*/
100+
@Property<Subscriber>({ type: 'string' })
101+
@Index<Subscriber>({ name: 'name' })
102+
@IsString()
103+
@MinLength(1)
104+
public name!: string
105+
106+
/**
107+
* Unix timestamp indicating when subscriber was last modified.
108+
*
109+
* @public
110+
* @readonly
111+
* @member {Nullable<number>} updated_at
112+
*/
113+
@Property<Subscriber>({ nullable: true, type: 'integer' })
114+
@IsTimestamp()
115+
@IsNullable()
116+
public updated_at: Nullable<number> = null
117+
118+
/**
119+
* Creates a new subscriber entity.
120+
*
121+
* @param {SubscriberDTO} dto - Data transfer object
122+
* @param {string} dto.email - Subscriber email
123+
* @param {string} dto.name - Subscriber name
124+
*/
125+
constructor({ _id, created_at, name, email, updated_at }: SubscriberDTO) {
126+
if (!isNIL(_id)) {
127+
this._id = _id
128+
this.id = this._id.toString()
129+
}
130+
131+
!isNIL(created_at) && (this.created_at = created_at)
132+
!isNIL(updated_at) && (this.updated_at = updated_at)
133+
134+
this.email = email.trim().toLowerCase()
135+
this.name = name.trim()
136+
}
137+
}
138+
139+
export default Subscriber

__fixtures__/subscriber.factory.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* @file Fixtures - SubscriberFactory
3+
* @module fixtures/SubscriberFactory
4+
*/
5+
6+
import type { Constructor } from '@mikro-orm/core'
7+
import type { MongoEntityManager } from '@mikro-orm/mongodb'
8+
import { Factory, Faker } from '@mikro-orm/seeder'
9+
import Subscriber from './subscriber.entity'
10+
11+
/**
12+
* Subscriber entity factory.
13+
*
14+
* @see {@linkcode Subscriber}
15+
*
16+
* @class
17+
* @extends {Factory<Subscriber>}
18+
*/
19+
class SubscriberFactory extends Factory<Subscriber> {
20+
/**
21+
* Entity the factory generates entity instances for.
22+
*
23+
* @public
24+
* @readonly
25+
* @member {Constructor<Subscriber>} model
26+
*/
27+
public readonly model: Constructor<Subscriber>
28+
29+
/**
30+
* Creates a new {@linkcode Subscriber} entity factory.
31+
*
32+
* @param {MongoEntityManager} em - Entity manager
33+
*/
34+
constructor(em: MongoEntityManager) {
35+
super(em)
36+
this.model = Subscriber
37+
}
38+
39+
/**
40+
* Returns the default set of attribute values that should be applied when
41+
* creating an {@linkcode Subscriber} entity.
42+
*
43+
* @see https://fakerjs.dev
44+
*
45+
* @protected
46+
* @override
47+
*
48+
* @param {Faker} faker - Faker library
49+
* @return {Pick<Subscriber, 'email' | 'name'>} Subscriber entity data
50+
*/
51+
protected override definition(
52+
faker: Faker
53+
): Pick<Subscriber, 'email' | 'name'> {
54+
return {
55+
email: faker.internet.email().toLowerCase(),
56+
name: faker.person.firstName()
57+
}
58+
}
59+
}
60+
61+
export default SubscriberFactory

__tests__/setup/chai.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
* @see https://chaijs.com
55
*/
66

7+
import chaiEach from 'chai-each'
78
import chaiHttp from 'chai-http'
89
import { chai } from 'vitest'
910

1011
/**
1112
* initialize chai plugins.
1213
*
14+
* @see https://github.com/jamesthomasonjr/chai-each
1315
* @see https://github.com/chaijs/chai-http
1416
*/
17+
chai.use(chaiEach)
1518
chai.use(chaiHttp)

0 commit comments

Comments
 (0)