@typetron/framework 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc-old.js +208 -0
- package/Cache/Cache.ts +13 -0
- package/Cache/Stores/DatabaseStore.ts +120 -0
- package/Cache/Stores/FileStore.ts +69 -0
- package/Cache/Stores/MemoryStore.ts +71 -0
- package/Cache/index.ts +5 -0
- package/Cache/types.ts +4 -0
- package/Container/ClassResolver.ts +106 -0
- package/Container/Container.ts +144 -0
- package/Container/Contracts.ts +3 -0
- package/Container/Decorators.ts +20 -0
- package/Container/Metadata.ts +16 -0
- package/Container/Resolver.ts +27 -0
- package/Container/index.ts +5 -0
- package/Database/Clauses/Where.ts +0 -0
- package/Database/Connection.ts +52 -0
- package/Database/Decorators.ts +142 -0
- package/Database/Drivers/DatabaseDriver.ts +37 -0
- package/Database/Drivers/MySQL/Alter/index.ts +55 -0
- package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
- package/Database/Drivers/MySQL/Create.ts +47 -0
- package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
- package/Database/Drivers/MySQL/Schema.ts +82 -0
- package/Database/Drivers/MySQL/index.ts +13 -0
- package/Database/Drivers/SQL/Schema.ts +73 -0
- package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
- package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
- package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
- package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
- package/Database/Drivers/SQL/Statements/Select.ts +55 -0
- package/Database/Drivers/SQL/Statements/Update.ts +33 -0
- package/Database/Drivers/SQL/index.ts +8 -0
- package/Database/Drivers/SQLite/Create.ts +45 -0
- package/Database/Drivers/SQLite/Schema.ts +54 -0
- package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
- package/Database/Drivers/SQLite/index.ts +15 -0
- package/Database/Drivers/SchemaContract.ts +11 -0
- package/Database/Drivers/Statement.ts +16 -0
- package/Database/Drivers/index.ts +4 -0
- package/Database/Entity.ts +377 -0
- package/Database/EntityNotFoundError.ts +5 -0
- package/Database/EntityProxyHandler.ts +43 -0
- package/Database/EntityQuery.ts +197 -0
- package/Database/Expression.ts +3 -0
- package/Database/Fields.ts +816 -0
- package/Database/Helpers.ts +12 -0
- package/Database/List.ts +71 -0
- package/Database/Migrations/Migration.ts +10 -0
- package/Database/Migrations/MigrationHistory.ts +20 -0
- package/Database/Migrations/Migrator.ts +122 -0
- package/Database/Migrations/index.ts +3 -0
- package/Database/ORM/BaseRelationship.ts +10 -0
- package/Database/Query.ts +374 -0
- package/Database/Seeders/Seeder.ts +3 -0
- package/Database/Seeders/SeederManager.ts +27 -0
- package/Database/Seeders/index.ts +2 -0
- package/Database/StringExpression.ts +9 -0
- package/Database/Types.ts +165 -0
- package/Database/index.ts +23 -0
- package/Encryption/index.ts +51 -0
- package/Forms/Decorators.ts +29 -0
- package/Forms/Form.ts +70 -0
- package/Forms/FormFields.ts +36 -0
- package/Forms/index.ts +3 -0
- package/Framework/App.ts +10 -0
- package/Framework/Application.ts +118 -0
- package/Framework/Auth/Auth.ts +122 -0
- package/Framework/Auth/Authenticatable.ts +9 -0
- package/Framework/Auth/User.ts +27 -0
- package/Framework/Auth/index.ts +29 -0
- package/Framework/Config/AppConfig.ts +25 -0
- package/Framework/Config/AuthConfig.ts +9 -0
- package/Framework/Config/BaseConfig.ts +9 -0
- package/Framework/Config/CacheConfig.ts +38 -0
- package/Framework/Config/DatabaseConfig.ts +21 -0
- package/Framework/Config/MailConfig.ts +30 -0
- package/Framework/Config/index.ts +6 -0
- package/Framework/Middleware/AuthMiddleware.ts +31 -0
- package/Framework/Middleware/CorsMiddleware.ts +32 -0
- package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
- package/Framework/Middleware/index.ts +2 -0
- package/Framework/Provider.ts +9 -0
- package/Framework/Providers/CacheProvider.ts +28 -0
- package/Framework/Providers/DatabaseProvider.ts +36 -0
- package/Framework/Providers/MailProvider.ts +31 -0
- package/Framework/Providers/WebsocketsProvider.ts +120 -0
- package/Framework/Resolvers/AuthResolver.ts +23 -0
- package/Framework/Resolvers/EntityResolver.ts +41 -0
- package/Framework/Resolvers/FormResolver.ts +37 -0
- package/Framework/RootDir.ts +1 -0
- package/Framework/index.ts +8 -0
- package/Frontend/Angular/index.ts +24 -0
- package/Frontend/Websockets/index.ts +86 -0
- package/Mail/MailAddress.ts +4 -0
- package/Mail/MailTransport.ts +6 -0
- package/Mail/Mailable.ts +16 -0
- package/Mail/Mailer.ts +27 -0
- package/Mail/PendingMail.ts +68 -0
- package/Mail/SentMessage.ts +20 -0
- package/Mail/Transports/MemoryTransport.ts +13 -0
- package/Mail/Transports/SendGridTransport.ts +95 -0
- package/Mail/index.ts +3 -0
- package/Models/Model.ts +41 -0
- package/Models/index.ts +54 -0
- package/Router/BaseController.ts +7 -0
- package/Router/Decorators.ts +224 -0
- package/Router/Guard.ts +10 -0
- package/Router/Http/Contracts.ts +4 -0
- package/Router/Http/ErrorHandler.ts +45 -0
- package/Router/Http/ErrorHandlerInterface.ts +6 -0
- package/Router/Http/Errors/HttpError.ts +15 -0
- package/Router/Http/Handler.ts +183 -0
- package/Router/Http/HttpRoute.ts +110 -0
- package/Router/Http/Middleware.ts +7 -0
- package/Router/Http/Request.ts +225 -0
- package/Router/Http/Response.ts +35 -0
- package/Router/Http/index.ts +85 -0
- package/Router/Metadata.ts +34 -0
- package/Router/Middleware.ts +16 -0
- package/Router/Request.ts +65 -0
- package/Router/Route.ts +16 -0
- package/Router/RouteNotFoundError.ts +5 -0
- package/Router/Router.ts +35 -0
- package/Router/Servers/index.ts +6 -0
- package/Router/Servers/node.ts +49 -0
- package/Router/Servers/uNetworking.ts +151 -0
- package/Router/Websockets/Handler.ts +82 -0
- package/Router/Websockets/Middleware.ts +7 -0
- package/Router/Websockets/Request.ts +3 -0
- package/Router/Websockets/WebsocketRoute.ts +61 -0
- package/Router/Websockets/index.ts +57 -0
- package/Router/Websockets/types.ts +23 -0
- package/Router/index.ts +6 -0
- package/Storage/File.ts +30 -0
- package/Storage/Image.ts +19 -0
- package/Storage/InvalidImage.ts +3 -0
- package/Storage/Storage.ts +98 -0
- package/Storage/index.ts +4 -0
- package/Storage/utils.ts +18 -0
- package/Support/Array.ts +184 -0
- package/Support/Math.ts +12 -0
- package/Support/Metadata.ts +31 -0
- package/Support/String.ts +38 -0
- package/Support/index.ts +19 -0
- package/Support/utils.ts +7 -0
- package/Testing/TestCase.ts +193 -0
- package/Validation/Rule.ts +9 -0
- package/Validation/RuleInterface.ts +10 -0
- package/Validation/Rules/Email.ts +20 -0
- package/Validation/Rules/FileExtension.ts +21 -0
- package/Validation/Rules/MaxLength.ts +17 -0
- package/Validation/Rules/MinLength.ts +18 -0
- package/Validation/Rules/Optional.ts +18 -0
- package/Validation/Rules/Required.ts +29 -0
- package/Validation/Validator.ts +7 -0
- package/Validation/index.ts +12 -0
- package/benchmark-index.js +20 -0
- package/benchmark.ts +47 -0
- package/package.json +7 -3
- package/tests/Cache/DatabaseStoreTest.ts +110 -0
- package/tests/Cache/FileStoreTest.ts +93 -0
- package/tests/Cache/MemoryStoreTest.ts +92 -0
- package/tests/Container/ContainerTest.ts +336 -0
- package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
- package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
- package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
- package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
- package/tests/Database/Entities/Article.ts +21 -0
- package/tests/Database/Entities/Profile.ts +21 -0
- package/tests/Database/Entities/Role.ts +15 -0
- package/tests/Database/Entities/User.ts +35 -0
- package/tests/Database/EntityRelationshipsTest.ts +475 -0
- package/tests/Database/EntityTest.ts +202 -0
- package/tests/Database/Migrations/RunnerTest.ts +96 -0
- package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
- package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
- package/tests/Database/QueryTest.ts +573 -0
- package/tests/Database/SchemaTest.ts +32 -0
- package/tests/Database/Seeders/RunnerTest.ts +30 -0
- package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
- package/tests/Database/utils.ts +16 -0
- package/tests/Forms/FormsTest.ts +59 -0
- package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
- package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
- package/tests/Framework/Router/RouterTest.ts +33 -0
- package/tests/Mail/MailerTest.ts +76 -0
- package/tests/Model/ModelTest.ts +183 -0
- package/tests/Router/HttpHandlerTest.ts +72 -0
- package/tests/Router/RequestTest.ts +77 -0
- package/tests/Router/WebSocketsHandlerTest.ts +51 -0
- package/tests/Storage/StorageTest.ts +97 -0
- package/tests/Support/ArrayTest.ts +167 -0
- package/tests/Support/StringTest.ts +18 -0
- package/tests/Validation/RulesTest.ts +54 -0
- package/types.d.ts +8 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AbstractControl, ValidatorFn } from '@angular/forms'
|
|
2
|
+
import { FormControl, FormGroup } from '@angular/forms'
|
|
3
|
+
import type { Form, FormField } from '@Typetron/Forms'
|
|
4
|
+
import { Constructor } from '@Typetron/Support'
|
|
5
|
+
|
|
6
|
+
export class FormBuilder {
|
|
7
|
+
static build<T extends Form>(form: typeof Form & Constructor<T>): FormGroup {
|
|
8
|
+
const controls: Record<string, AbstractControl> = {}
|
|
9
|
+
const formFields = Object.values(form.fields()) as FormField[]
|
|
10
|
+
const instance = new (form as unknown as Constructor<T>)()
|
|
11
|
+
Object.values(formFields).forEach(field => {
|
|
12
|
+
controls[field.name] = new FormControl(
|
|
13
|
+
instance[field.name as keyof Form],
|
|
14
|
+
{validators: this.getValidators(field)}
|
|
15
|
+
)
|
|
16
|
+
})
|
|
17
|
+
return new FormGroup(controls)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static getValidators(field: FormField): ValidatorFn {
|
|
21
|
+
return control => field.validate(control.value) as unknown as ValidatorFn
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'
|
|
2
|
+
import { filter, map, switchMap, take } from 'rxjs/operators'
|
|
3
|
+
import { ActionErrorResponse, ActionRequest, ActionResponse, WebsocketMessageStatus } from '@typetron/framework/Router/Websockets/types'
|
|
4
|
+
|
|
5
|
+
export class Websocket {
|
|
6
|
+
socket?: WebSocket
|
|
7
|
+
|
|
8
|
+
actionMessages$ = new Subject<ActionResponse<unknown>>()
|
|
9
|
+
queuedActions$ = new ReplaySubject<ActionRequest>()
|
|
10
|
+
errors$ = new Subject<ActionErrorResponse>()
|
|
11
|
+
onConnectCallback: () => void
|
|
12
|
+
|
|
13
|
+
constructor(public url: string, public protocols?: string | string[]) {
|
|
14
|
+
this.connect(url, protocols)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get isConnected() {
|
|
18
|
+
return this.socket?.readyState === WebSocket.OPEN
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
connect(url?: string, protocols?: string | string[]): void {
|
|
22
|
+
const socket = new WebSocket(url ?? this.url, protocols ?? this.protocols)
|
|
23
|
+
this.socket = socket
|
|
24
|
+
|
|
25
|
+
socket.onmessage = (action) => {
|
|
26
|
+
const message = JSON.parse(action.data)
|
|
27
|
+
this.actionMessages$.next(message)
|
|
28
|
+
}
|
|
29
|
+
socket.onopen = () => {
|
|
30
|
+
this.onConnectCallback?.()
|
|
31
|
+
this.queuedActions$.subscribe(message => {
|
|
32
|
+
socket.send(JSON.stringify(message))
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
socket.onclose = (action) => {
|
|
37
|
+
this.queuedActions$ = new ReplaySubject<ActionRequest>()
|
|
38
|
+
this.reconnect()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onConnect(callback: () => void) {
|
|
43
|
+
this.onConnectCallback = callback
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
reconnect(): void {
|
|
47
|
+
if (this.socket?.readyState === WebSocket.CLOSED) {
|
|
48
|
+
window.setTimeout(() => {
|
|
49
|
+
this.connect(this.url, this.protocols)
|
|
50
|
+
}, 1000)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
emit<T>(action: string, request?: ActionRequest['message']): void {
|
|
55
|
+
const actionMessage: ActionRequest = {action, message: request}
|
|
56
|
+
this.queuedActions$.next(actionMessage)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
on<T>(action: string): Observable<T> {
|
|
60
|
+
return this.actionMessages$.pipe(
|
|
61
|
+
filter(actionMessage => actionMessage.action === action),
|
|
62
|
+
switchMap(actionMessage => {
|
|
63
|
+
if (actionMessage.status === WebsocketMessageStatus.Error) {
|
|
64
|
+
this.errors$.next(actionMessage as ActionErrorResponse)
|
|
65
|
+
return throwError(actionMessage)
|
|
66
|
+
}
|
|
67
|
+
return of(actionMessage)
|
|
68
|
+
}),
|
|
69
|
+
map(actionMessage => actionMessage.message)
|
|
70
|
+
) as Observable<T>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onError({except}: {except?: string[]}): Observable<ActionResponse<unknown>> {
|
|
74
|
+
return this.errors$.pipe(filter(actionMessage => !(except || []).includes(actionMessage.action)))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async request<T>(action: string, message?: ActionRequest['message']): Promise<T> {
|
|
78
|
+
this.emit(action, message)
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
this.on<T>(action).pipe(take(1)).subscribe({
|
|
81
|
+
next: resolve,
|
|
82
|
+
error: reject
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
package/Mail/Mailable.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MailAddress } from '@Typetron/Mail/MailAddress'
|
|
2
|
+
|
|
3
|
+
export class Mailable {
|
|
4
|
+
|
|
5
|
+
from: string | MailAddress
|
|
6
|
+
subject?: string
|
|
7
|
+
body?: string | {html: string, text: string}
|
|
8
|
+
to?: string | MailAddress | (string | MailAddress)[]
|
|
9
|
+
replyTo?: string | MailAddress | (string | MailAddress)[]
|
|
10
|
+
cc?: string | MailAddress | (string | MailAddress)[]
|
|
11
|
+
bcc?: string | MailAddress | (string | MailAddress)[]
|
|
12
|
+
|
|
13
|
+
content(): string | {html: string, text: string} | undefined {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
}
|
package/Mail/Mailer.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MailTransport } from './MailTransport'
|
|
2
|
+
import { PendingMail } from './PendingMail'
|
|
3
|
+
import { Mailable } from './Mailable'
|
|
4
|
+
import { MailAddress } from '@Typetron/Mail/MailAddress'
|
|
5
|
+
|
|
6
|
+
export class Mailer {
|
|
7
|
+
transport: MailTransport
|
|
8
|
+
|
|
9
|
+
constructor(public from: MailAddress, transport?: MailTransport) {
|
|
10
|
+
if (!transport) {
|
|
11
|
+
throw new Error('Please add the MailProvider in your app in order to use the Mailing feature, ' +
|
|
12
|
+
'or manually add a Mailer instance in the app container ')
|
|
13
|
+
}
|
|
14
|
+
this.transport = transport
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
to(email: string) {
|
|
18
|
+
const mail = new PendingMail(this)
|
|
19
|
+
mail.from(this.from)
|
|
20
|
+
mail.to(email)
|
|
21
|
+
return mail
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async send(message: Mailable) {
|
|
25
|
+
return await this.transport.send(message)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Mailer } from './index'
|
|
2
|
+
import { Mailable } from './Mailable'
|
|
3
|
+
import { MailAddress } from '@Typetron/Mail/MailAddress'
|
|
4
|
+
|
|
5
|
+
export class PendingMail {
|
|
6
|
+
|
|
7
|
+
details: {
|
|
8
|
+
subject?: string
|
|
9
|
+
from: string | MailAddress
|
|
10
|
+
to?: string | MailAddress
|
|
11
|
+
replyTo?: string | MailAddress
|
|
12
|
+
cc?: string | MailAddress
|
|
13
|
+
bcc?: string | MailAddress
|
|
14
|
+
body?: string | {html: string, text: string}
|
|
15
|
+
} = {from: ''}
|
|
16
|
+
|
|
17
|
+
constructor(private mailer: Mailer) {
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
subject(subject: string) {
|
|
21
|
+
this.details.subject = subject
|
|
22
|
+
return this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
from(email: string | MailAddress) {
|
|
26
|
+
this.details.from = email
|
|
27
|
+
return this
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
to(email: string | MailAddress) {
|
|
31
|
+
this.details.to = email
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
replyTo(email: string | MailAddress) {
|
|
36
|
+
this.details.replyTo = email
|
|
37
|
+
return this
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
cc(email: string | MailAddress) {
|
|
41
|
+
this.details.cc = email
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
bcc(email: string | MailAddress) {
|
|
46
|
+
this.details.bcc = email
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async send(body: string | {html: string, text: string} | Mailable) {
|
|
51
|
+
let mail
|
|
52
|
+
if (body instanceof Mailable) {
|
|
53
|
+
mail = body
|
|
54
|
+
} else {
|
|
55
|
+
mail = new Mailable()
|
|
56
|
+
mail.body = body
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
mail.subject = this.details.subject
|
|
60
|
+
mail.from = this.details.from
|
|
61
|
+
mail.to = this.details.to
|
|
62
|
+
mail.replyTo = this.details.replyTo
|
|
63
|
+
mail.cc = this.details.cc
|
|
64
|
+
mail.bcc = this.details.bcc
|
|
65
|
+
|
|
66
|
+
return this.mailer.send(mail)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MailAddress } from './MailAddress'
|
|
2
|
+
import { Mailable } from '@Typetron/Mail/Mailable'
|
|
3
|
+
|
|
4
|
+
export class SentMessage {
|
|
5
|
+
readonly from: string | MailAddress
|
|
6
|
+
readonly subject?: string
|
|
7
|
+
readonly body?: string | {html: string, text: string}
|
|
8
|
+
readonly to?: string | MailAddress | (string | MailAddress)[]
|
|
9
|
+
readonly replyTo?: string | MailAddress | (string | MailAddress)[]
|
|
10
|
+
readonly cc?: string | MailAddress | (string | MailAddress)[]
|
|
11
|
+
readonly bcc?: string | MailAddress | (string | MailAddress)[]
|
|
12
|
+
|
|
13
|
+
constructor(mail: Mailable) {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15
|
+
const {content, ...mailDetails} = mail
|
|
16
|
+
Object.assign(this, mailDetails)
|
|
17
|
+
this.body = mail.content() ?? mail.body
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MailTransport } from '../MailTransport'
|
|
2
|
+
import { SentMessage } from '../SentMessage'
|
|
3
|
+
import { Mailable } from '../Mailable'
|
|
4
|
+
|
|
5
|
+
export class MemoryTransport extends MailTransport {
|
|
6
|
+
messages: SentMessage[] = []
|
|
7
|
+
|
|
8
|
+
async send(message: Mailable) {
|
|
9
|
+
const sentMessage = new SentMessage(message)
|
|
10
|
+
this.messages.push(sentMessage)
|
|
11
|
+
return sentMessage
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { MailTransport } from '../MailTransport'
|
|
2
|
+
import { SentMessage } from '../SentMessage'
|
|
3
|
+
import { Mailable } from '../Mailable'
|
|
4
|
+
import { Inject } from '../../Container'
|
|
5
|
+
import { MailConfig } from '../../Framework'
|
|
6
|
+
import client from '@sendgrid/mail'
|
|
7
|
+
import { HttpError } from '@Typetron/Router/Http'
|
|
8
|
+
|
|
9
|
+
export class SendGridTransport extends MailTransport {
|
|
10
|
+
messages: SentMessage[] = []
|
|
11
|
+
|
|
12
|
+
@Inject()
|
|
13
|
+
config: MailConfig
|
|
14
|
+
|
|
15
|
+
constructor(key: string) {
|
|
16
|
+
super()
|
|
17
|
+
|
|
18
|
+
client.setApiKey(key)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async send(message: Mailable) {
|
|
22
|
+
const sentMessage = new SentMessage(message)
|
|
23
|
+
|
|
24
|
+
const sendGridMessage = {
|
|
25
|
+
personalizations: [
|
|
26
|
+
{
|
|
27
|
+
to: sentMessage.to,
|
|
28
|
+
cc: sentMessage.cc,
|
|
29
|
+
bcc: sentMessage.bcc
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
from: sentMessage.from,
|
|
33
|
+
replyTo: sentMessage.replyTo,
|
|
34
|
+
subject: sentMessage.subject,
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text/html',
|
|
38
|
+
value: sentMessage.body
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
// attachments: [
|
|
42
|
+
// {
|
|
43
|
+
// content: 'PCFET0NUdGxlPkRvY3VtZW50PC90aXRsZT4KICAgIDwvaGVhZD4KCiAgICA8Ym9keT4KCiAgICA8L2JvZHk+Cgo8L2h0bWw+Cg==',
|
|
44
|
+
// filename: 'index.html',
|
|
45
|
+
// type: 'text/html',
|
|
46
|
+
// disposition: 'attachment'
|
|
47
|
+
// }
|
|
48
|
+
// ],
|
|
49
|
+
// categories: [
|
|
50
|
+
// 'cake',
|
|
51
|
+
// 'pie',
|
|
52
|
+
// 'baking'
|
|
53
|
+
// ],
|
|
54
|
+
// sendAt: 1617260400,
|
|
55
|
+
// batchId: 'AsdFgHjklQweRTYuIopzXcVBNm0aSDfGHjklmZcVbNMqWert1znmOP2asDFjkl',
|
|
56
|
+
// asm: {
|
|
57
|
+
// groupId: 12345,
|
|
58
|
+
// groupsToDisplay: [
|
|
59
|
+
// 12345
|
|
60
|
+
// ]
|
|
61
|
+
// },
|
|
62
|
+
// ipPoolName: 'transactional email',
|
|
63
|
+
// mailSettings: {
|
|
64
|
+
// bypassListManagement: {
|
|
65
|
+
// enable: false
|
|
66
|
+
// },
|
|
67
|
+
// footer: {
|
|
68
|
+
// enable: false
|
|
69
|
+
// },
|
|
70
|
+
// sandboxMode: {
|
|
71
|
+
// enable: false
|
|
72
|
+
// }
|
|
73
|
+
// },
|
|
74
|
+
// trackingSettings: {
|
|
75
|
+
// clickTracking: {
|
|
76
|
+
// enable: true,
|
|
77
|
+
// enableText: false
|
|
78
|
+
// },
|
|
79
|
+
// openTracking: {
|
|
80
|
+
// enable: true,
|
|
81
|
+
// substitutionTag: '%open-track%'
|
|
82
|
+
// },
|
|
83
|
+
// subscriptionTracking: {
|
|
84
|
+
// enable: false
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
90
|
+
/* @ts-ignore */ // SendGrid has TS issues
|
|
91
|
+
await client.send(sendGridMessage).catch(error => {throw new HttpError(error.response.body.errors, error.code)})
|
|
92
|
+
|
|
93
|
+
return sentMessage
|
|
94
|
+
}
|
|
95
|
+
}
|
package/Mail/index.ts
ADDED
package/Models/Model.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Constructor } from '../Support'
|
|
2
|
+
import { ModelField, ModelMetadataKey, ModelTypeInterface } from './index'
|
|
3
|
+
|
|
4
|
+
export class Model {
|
|
5
|
+
static async from<T extends Model, Q extends object>(this: Constructor<T> & typeof Model, entity: Promise<Iterable<Q>>): Promise<T[]>;
|
|
6
|
+
static async from<T extends Model, Q extends object>(this: Constructor<T> & typeof Model, entity: Promise<Q>): Promise<T>;
|
|
7
|
+
static from<T extends Model, Q extends object>(this: Constructor<T> & typeof Model, entity: Iterable<Q>): T[];
|
|
8
|
+
static from<T extends Model, Q extends object>(this: Constructor<T> & typeof Model, entity: Q): T;
|
|
9
|
+
static from<T extends Model, Q extends object>(
|
|
10
|
+
this: Constructor<T> & typeof Model,
|
|
11
|
+
entity: Q | Iterable<Q> | Promise<Q | Iterable<Q>>
|
|
12
|
+
): T | T[] | Promise<T | T[]> {
|
|
13
|
+
if (entity instanceof Promise) {
|
|
14
|
+
return entity.then(value => this.from(value))
|
|
15
|
+
}
|
|
16
|
+
if (Symbol.iterator in Object(entity)) {
|
|
17
|
+
return Array.from(entity as Iterable<Q>).map(item => this.from<T, Q>(item))
|
|
18
|
+
}
|
|
19
|
+
const fields: Record<string, ModelField> = Reflect.getMetadata(ModelMetadataKey, this) || {}
|
|
20
|
+
|
|
21
|
+
return this.transform<T, Q>(fields, entity as Q)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected static transform<T, Q extends object>(fields: Record<string, ModelField>, entity: Q): T {
|
|
25
|
+
const data: Partial<Record<keyof T, Q[keyof Q]>> = new this
|
|
26
|
+
Object.values(fields).forEach(field => {
|
|
27
|
+
if (!entity || !entity.hasOwnProperty(field.name)) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
const value = entity[field.name as keyof Q]
|
|
31
|
+
if (field.type instanceof ModelTypeInterface && value) {
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
const jsonValue = value['toJSON' as keyof object] ? value?.toJSON() : value
|
|
34
|
+
data[field.name as keyof T] = jsonValue ? field.type.transform(jsonValue) as Q[keyof Q] : undefined
|
|
35
|
+
} else {
|
|
36
|
+
data[field.name as keyof T] = value
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
return data as T
|
|
40
|
+
}
|
|
41
|
+
}
|
package/Models/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Model } from './Model'
|
|
2
|
+
import { Constructor } from '../Support'
|
|
3
|
+
|
|
4
|
+
export const ModelMetadataKey = 'model:fields'
|
|
5
|
+
|
|
6
|
+
export interface ModelField {
|
|
7
|
+
name: string;
|
|
8
|
+
type: Function | ModelTypeInterface<{}>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Field(name?: string) {
|
|
12
|
+
return function(target: Object, property: string) {
|
|
13
|
+
const fields: Record<string, ModelField> = Reflect.getMetadata(ModelMetadataKey, target.constructor) || {}
|
|
14
|
+
let type = Reflect.getMetadata('design:type', target, property)
|
|
15
|
+
if (type.prototype instanceof Model) {
|
|
16
|
+
type = new ModelType(type)
|
|
17
|
+
}
|
|
18
|
+
fields[property] = fields[property] || {name: name || property, type} as ModelField
|
|
19
|
+
Reflect.defineMetadata(ModelMetadataKey, fields, target.constructor)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export abstract class ModelTypeInterface<T extends Model, Q = T> {
|
|
24
|
+
constructor(public model: Constructor<T> & typeof Model) {}
|
|
25
|
+
|
|
26
|
+
abstract transform(value: Q): Q;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class ModelType<T extends Model> extends ModelTypeInterface<T> {
|
|
30
|
+
|
|
31
|
+
transform(value: T) {
|
|
32
|
+
return this.model.from(value)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class ArrayType<T extends Model> extends ModelTypeInterface<T, T[]> {
|
|
37
|
+
transform(value: T[]) {
|
|
38
|
+
return Array.from(value).map(item => {
|
|
39
|
+
return this.model.from(item)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function FieldMany(type: typeof Model) {
|
|
45
|
+
return function(target: Object, property: string) {
|
|
46
|
+
const fields: Record<string, ModelField> = Reflect.getMetadata(ModelMetadataKey, target.constructor) || {}
|
|
47
|
+
const field = fields[property] || {name: property, type: new ArrayType(type)} as ModelField
|
|
48
|
+
field.type = new ArrayType(type)
|
|
49
|
+
fields[property] = field
|
|
50
|
+
Reflect.defineMetadata(ModelMetadataKey, fields, target.constructor)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export * from './Model'
|