@point-hub/papi 0.1.5 → 0.1.6

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.
@@ -0,0 +1,143 @@
1
+ import { SortDirection } from 'mongodb'
2
+
3
+ interface IFieldsObject {
4
+ [key: string]: number
5
+ }
6
+
7
+ interface ISortObject {
8
+ [key: string]: SortDirection
9
+ }
10
+
11
+ /**
12
+ * Parse query string to number
13
+ *
14
+ * @example
15
+ * page("10") // => 10
16
+ * page(10) // => 10
17
+ */
18
+ export function page(page: string | number = 1): number {
19
+ if (typeof page === 'string') {
20
+ return parseInt(page)
21
+ }
22
+
23
+ return page
24
+ }
25
+
26
+ /**
27
+ * Parse query string to number
28
+ *
29
+ * @example
30
+ * limit("10") // => 10
31
+ * limit(10) // => 10
32
+ */
33
+ export function limit(pageSize: string | number = 10): number {
34
+ if (typeof pageSize === 'string') {
35
+ return parseInt(pageSize)
36
+ }
37
+
38
+ return pageSize
39
+ }
40
+
41
+ /**
42
+ * Skip number of data from page
43
+ *
44
+ * @example
45
+ * skip(1, 10) // => 0
46
+ * skip(2, 10) // => 10
47
+ * skip(3, 10) // => 20
48
+ */
49
+ export function skip(currentPage: string | number, pageSize: string | number): number {
50
+ return (page(currentPage) - 1) * limit(pageSize)
51
+ }
52
+
53
+ export function filter(filter: never) {
54
+ return filter
55
+ }
56
+
57
+ /**
58
+ * Convert string param to mongodb field object
59
+ *
60
+ * @example
61
+ * fields("name, address") // => { name: 1, address: 1 }
62
+ */
63
+ export function fields(fields = '', excludeFields: string[] = []): IFieldsObject {
64
+ const obj: IFieldsObject = convertArrayToObject(convertStringToArray(fields))
65
+
66
+ return filterExludeFields(obj, excludeFields)
67
+ }
68
+
69
+ /**
70
+ * Convert string to array
71
+ *
72
+ * @example
73
+ * convertStringToArray("name, address") // => ["name", "address"]
74
+ */
75
+ export function convertStringToArray(fields: string): string[] {
76
+ return fields
77
+ .split(' ')
78
+ .join()
79
+ .split(',')
80
+ .filter((el) => el)
81
+ }
82
+
83
+ /**
84
+ * Convert array to mongodb field object
85
+ *
86
+ * @example
87
+ * convertArrayToObject(["name", "address"]) // => { name: 1, address: 1 }
88
+ * convertArrayToObject(["name", "address"], -1) // => { name: -1, address: -1 }
89
+ */
90
+ export function convertArrayToObject(array: string[], value = 1): IFieldsObject {
91
+ const obj: IFieldsObject = {}
92
+ for (let i = 0; i < array.length; i++) {
93
+ obj[`${array[i].trim()}`] = value
94
+ }
95
+ return obj
96
+ }
97
+
98
+ /**
99
+ * Remove excluded fields
100
+ *
101
+ * @example
102
+ * ex: { password: 0 }
103
+ */
104
+ export function filterExludeFields(obj: IFieldsObject, excludeFields: string[]): IFieldsObject {
105
+ for (let i = 0; i < excludeFields.length; i++) {
106
+ obj[`${excludeFields[i]}`] = 0
107
+ }
108
+ return obj
109
+ }
110
+
111
+ /**
112
+ * Convert string param to mongodb sort object
113
+ *
114
+ * @example
115
+ * sort("name, -createdAt") // => { name: 1, createdAt: -1 }
116
+ */
117
+ export function sort(fields: string): ISortObject {
118
+ const obj: ISortObject = {}
119
+
120
+ if (fields) {
121
+ fields.split(',').forEach(function (field) {
122
+ if (field.charAt(0) === '-') {
123
+ field = field.substring(1)
124
+ obj[field.trim()] = -1
125
+ } else {
126
+ obj[field.trim()] = 1
127
+ }
128
+ })
129
+ }
130
+ return obj
131
+ }
132
+
133
+ export default {
134
+ page,
135
+ limit,
136
+ skip,
137
+ sort,
138
+ fields,
139
+ filter,
140
+ filterExludeFields,
141
+ convertStringToArray,
142
+ convertArrayToObject,
143
+ }
package/src/index.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { BaseCommand } from '@point-hub/express-cli'
2
+ import {
3
+ ApiError,
4
+ BaseError,
5
+ errorHandlerMiddleware,
6
+ find,
7
+ invalidPathMiddleware,
8
+ isTrustedError,
9
+ } from '@point-hub/express-error-handler'
10
+ import { MongoServerError } from 'mongodb'
11
+
12
+ import { ConsoleKernel } from './console'
13
+ import { DatabaseConnection } from './database/connection'
14
+ import { MongoDBConnection } from './database/mongodb/connection'
15
+ import { MongoErrorHandler } from './database/mongodb/mongodb-error-handler'
16
+ import { MongoDBHelper } from './database/mongodb/mongodb-helper'
17
+ import Querystring from './database/mongodb/mongodb-querystring'
18
+
19
+ export const stubDir = import.meta.path.replace('/index.ts', '/../stub').replace('/index.js', '/../stub')
20
+
21
+ // Console
22
+ export { ExpressCli as BaseConsoleCli } from '@point-hub/express-cli'
23
+ export const BaseConsoleCommand = BaseCommand
24
+ export const BaseConsoleKernel = ConsoleKernel
25
+ // Database
26
+ export const BaseDatabaseConnection = DatabaseConnection
27
+ export const BaseMongoDBConnection = MongoDBConnection
28
+ export const BaseMongoDBHelper = MongoDBHelper
29
+ export const BaseMongoDBQuerystring = Querystring
30
+ export const BaseMongoServerError = MongoServerError
31
+ export const BaseMongoErrorHandler = MongoErrorHandler
32
+ // Error Handler
33
+ export const BaseErrorHandler = {
34
+ ApiError,
35
+ BaseError,
36
+ isTrustedError,
37
+ getHttpError: find,
38
+ errorHandlerMiddleware,
39
+ invalidPathMiddleware,
40
+ }
41
+ // Server
42
+ export { Server as BaseServer } from './server'
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+
3
+ import { createApp } from './app'
4
+ import { BaseServer as Server } from './index'
5
+
6
+ const port = 3001
7
+
8
+ describe('server', () => {
9
+ it('server should start on http://localhost:port', async () => {
10
+ const server = new Server(await createApp())
11
+
12
+ await server.start(port)
13
+
14
+ expect(server.host).toEqual('localhost')
15
+ expect(server.port).toEqual(port)
16
+ expect(server.url).toEqual(`http://localhost:${port}`)
17
+
18
+ server.stop()
19
+ })
20
+
21
+ it('get.url should not print out port if port = 80', async () => {
22
+ const server = new Server(await createApp())
23
+
24
+ await server.start(80)
25
+
26
+ expect(server.url).toEqual(`http://localhost`)
27
+
28
+ server.stop()
29
+ })
30
+
31
+ it('get.host return original ip', async () => {
32
+ const server = new Server(await createApp())
33
+ await server.start(port, '127.0.0.1')
34
+ expect(server.host).toEqual('127.0.0.1')
35
+ server.stop()
36
+ })
37
+
38
+ it('get.host return localhost', async () => {
39
+ const server = new Server(await createApp())
40
+ await server.start(port)
41
+ expect(server.host).toEqual('localhost')
42
+ server.stop()
43
+ })
44
+
45
+ it('not listening server should return undefined', async () => {
46
+ const server = new Server(await createApp())
47
+ server.stop()
48
+ expect(server.host).toBeUndefined()
49
+ expect(server.port).toBeUndefined()
50
+ expect(server.url).toEqual('http://undefined:undefined')
51
+ })
52
+
53
+ it('using port that already in use return error', async () => {
54
+ const server1 = new Server(await createApp())
55
+ const server2 = new Server(await createApp())
56
+
57
+ try {
58
+ await server1.start(port)
59
+
60
+ // start another server using same port to invoke error
61
+ await server2.start(port)
62
+ } catch (error) {
63
+ expect(error).toBeDefined()
64
+ server1.stop()
65
+ server2.stop()
66
+ }
67
+ })
68
+ })
package/src/server.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { Express } from 'express'
2
+ import { Server as HttpServer } from 'http'
3
+ import { AddressInfo } from 'net'
4
+
5
+ export class Server {
6
+ app: Express
7
+ server: HttpServer | null = null
8
+
9
+ constructor(app: Express) {
10
+ this.app = app
11
+ }
12
+
13
+ listen(port: number, hostname?: string) {
14
+ return new Promise((resolve, reject) => {
15
+ if (hostname) {
16
+ this.server = this.app.listen(port, hostname).once('listening', resolve).once('error', reject)
17
+ } else {
18
+ this.server = this.app.listen(port).once('listening', resolve).once('error', reject)
19
+ }
20
+ })
21
+ }
22
+
23
+ async start(port: number, hostname?: string) {
24
+ await this.listen(port, hostname)
25
+ }
26
+
27
+ stop() {
28
+ this.server?.close()
29
+ this.server = null
30
+ }
31
+
32
+ get host() {
33
+ const address = this.server?.address() as AddressInfo
34
+ if (address?.address === '0.0.0.0' || address?.address === '::') {
35
+ return 'localhost'
36
+ } else {
37
+ return address?.address
38
+ }
39
+ }
40
+
41
+ get port() {
42
+ const address = this.server?.address() as AddressInfo
43
+ return address?.port
44
+ }
45
+
46
+ get url() {
47
+ return `http://${this.host}${this.port !== 80 ? `:${this.port}` : ''}`
48
+ }
49
+ }
@@ -0,0 +1,18 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ interface IHttpRequest {
3
+ [key: string]: any
4
+ }
5
+
6
+ interface IControllerInput {
7
+ httpRequest: IHttpRequest
8
+ dbConnection: IDatabase
9
+ }
10
+
11
+ interface IController {
12
+ (input: IControllerInput): Promise<IControllerOutput>
13
+ }
14
+
15
+ interface IControllerOutput {
16
+ status: number
17
+ json?: any
18
+ }
@@ -0,0 +1,104 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ interface IDocument {
3
+ [key: string]: any
4
+ }
5
+
6
+ interface IQuery {
7
+ fields?: string
8
+ excludeFields?: string[]
9
+ filter?: { [key: string]: unknown }
10
+ page?: number
11
+ pageSize?: number
12
+ sort?: string
13
+ }
14
+
15
+ interface IPipeline {
16
+ [key: string]: any
17
+ }
18
+
19
+ interface ICreateOutput {
20
+ insertedId: string
21
+ }
22
+
23
+ interface ICreateManyOutput {
24
+ insertedCount: number
25
+ insertedIds: string[]
26
+ }
27
+
28
+ interface IRetrieveOutput {
29
+ _id: string
30
+ [key: string]: unknown
31
+ }
32
+
33
+ interface IRetrieveAllOutput {
34
+ data: IRetrieveOutput[]
35
+ pagination: {
36
+ page: number
37
+ pageCount: number
38
+ pageSize: number
39
+ totalDocument: number
40
+ }
41
+ }
42
+
43
+ interface IUpdateOutput {
44
+ matchedCount: number
45
+ modifiedCount: number
46
+ }
47
+
48
+ interface IUpdateManyOutput {
49
+ matchedCount: number
50
+ modifiedCount: number
51
+ }
52
+
53
+ interface IDeleteOutput {
54
+ deletedCount: number
55
+ }
56
+
57
+ interface IDeleteManyOutput {
58
+ deletedCount: number
59
+ }
60
+
61
+ interface IAggregateOutput {
62
+ data: IRetrieveOutput[]
63
+ pagination: {
64
+ page: number
65
+ pageCount: number
66
+ pageSize: number
67
+ totalDocument: number
68
+ }
69
+ }
70
+
71
+ interface IClientSession {
72
+ startTransaction(options?: unknown): void
73
+ commitTransaction(): Promise<void>
74
+ abortTransaction(): Promise<void>
75
+ endSession(): Promise<void>
76
+ }
77
+
78
+ interface IDatabase {
79
+ session: unknown
80
+ open(): Promise<void>
81
+ close(): Promise<void>
82
+ database(name: string, options?: unknown): this
83
+ collection(name: string, options?: unknown): this
84
+ listCollections(): Promise<{ name: string }[]>
85
+ startSession(): IClientSession
86
+ endSession(): Promise<void>
87
+ startTransaction(): void
88
+ commitTransaction(): Promise<void>
89
+ abortTransaction(): Promise<void>
90
+ createIndex(name: string, spec: unknown, options?: unknown): Promise<void>
91
+ createCollection(name: string, options?: unknown): Promise<void>
92
+ dropCollection(name: string, options?: unknown): Promise<void>
93
+ updateSchema(name: string, schema: unknown): Promise<void>
94
+ create(document: IDocument, options?: unknown): Promise<ICreateOutput>
95
+ createMany(documents: IDocument[], options?: unknown): Promise<ICreateManyOutput>
96
+ retrieveAll(query: IQuery, options?: unknown): Promise<IRetrieveAllOutput>
97
+ retrieve(_id: string, options?: unknown): Promise<IRetrieveOutput>
98
+ update(_id: string, document: IDocument, options?: unknown): Promise<IUpdateOutput>
99
+ updateMany(filter: IDocument, document: IDocument, options?: unknown): Promise<IUpdateManyOutput>
100
+ delete(_id: string, options?: unknown): Promise<IDeleteOutput>
101
+ deleteMany(_ids: string[], options?: unknown): Promise<IDeleteManyOutput>
102
+ deleteAll(options?: unknown): Promise<IDeleteManyOutput>
103
+ aggregate(pipeline: IPipeline, query: IQuery, options?: unknown): Promise<IAggregateOutput>
104
+ }
@@ -0,0 +1,3 @@
1
+ interface IUseCase<TInput, TDeps, TOptions, TOutput> {
2
+ handle(input: TInput, deps: TDeps, options?: TOptions): Promise<TOutput>
3
+ }