diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..0b330e2 --- /dev/null +++ b/index.ts @@ -0,0 +1,147 @@ +import type { + CompleteHandler, + ErrorHandler, + Handlers, + HTTP_METHOD, + HTTP_STATUS, + NextHandler, + ObservableLike, + ObserverLike, + Request, + RequestError, + Subscription, + SubscribeFn, + Unsubscribe, + User, +} from './types'; + +class Observer implements ObserverLike { + private handlers: Handlers; + private isUnsubscribed: boolean; + public _unsubscribe?: Unsubscribe; + + constructor(handlers: Handlers) { + this.handlers = handlers; + this.isUnsubscribed = false; + } + + next(value: Request): void { + if (this.handlers.next && !this.isUnsubscribed) { + this.handlers.next(value); + } + } + + error(error: RequestError): void { + if (!this.isUnsubscribed) { + if (this.handlers.error) { + this.handlers.error(error); + } + + this.unsubscribe(); + } + } + + complete(): void { + if (!this.isUnsubscribed) { + if (this.handlers.complete) { + this.handlers.complete(); + } + + this.unsubscribe(); + } + } + + unsubscribe(): void { + this.isUnsubscribed = true; + + if (this._unsubscribe) { + this._unsubscribe(); + } + } +} + +class Observable implements ObservableLike { + private _subscribe: SubscribeFn; + + constructor(subscribe: SubscribeFn) { + this._subscribe = subscribe; + } + + static from(values: Request[]): Observable { + return new Observable((observer: ObserverLike): Unsubscribe => { + values.forEach((value: Request) => observer.next(value)); + + observer.complete(); + + return (): void => { + console.log('unsubscribed'); + }; + }); + } + + subscribe(obs: Handlers): Subscription { + const observer = new Observer(obs); + + observer._unsubscribe = this._subscribe(observer); + + return { + unsubscribe(): void { + observer.unsubscribe(); + }, + }; + } +} + +const HTTP_POST_METHOD: HTTP_METHOD = 'POST'; +const HTTP_GET_METHOD: HTTP_METHOD = 'GET'; + +const HTTP_STATUS_OK: HTTP_STATUS = 200; +const HTTP_STATUS_INTERNAL_SERVER_ERROR: HTTP_STATUS = 500; + +const userMock: User = { + name: 'User Name', + age: 26, + roles: ['user', 'admin'], + createdAt: new Date(), + isDeleated: false, +}; + +const requestsMock: Request[] = [ + { + method: HTTP_POST_METHOD, + host: 'service.example', + path: 'user', + body: userMock, + params: {}, + }, + { + method: HTTP_GET_METHOD, + host: 'service.example', + path: 'user', + params: { + id: '3f5h67s4s', + }, + }, +]; + +const handleRequest: NextHandler = (request: Request) => { + // handling of request + return { status: HTTP_STATUS_OK }; +}; + +const handleError: ErrorHandler = (error: RequestError) => { + // handling of error + return { status: HTTP_STATUS_INTERNAL_SERVER_ERROR }; +}; + +const handleComplete: CompleteHandler = () => console.log('complete'); + +const requests$: ObservableLike = Observable.from(requestsMock); + +const subscription: Subscription = requests$.subscribe({ + next: handleRequest, + error: handleError, + complete: handleComplete, +}); + +subscription.unsubscribe(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..512595e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "noEmit": true, + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "skipLibCheck": true + }, + "include": ["**/*.ts"] +} diff --git a/types/handlers.ts b/types/handlers.ts new file mode 100644 index 0000000..334332a --- /dev/null +++ b/types/handlers.ts @@ -0,0 +1,11 @@ +import type { Request, RequestError, HandlerResponse } from './request'; + +export type NextHandler = (request: Request) => HandlerResponse; +export type ErrorHandler = (error: RequestError) => HandlerResponse; +export type CompleteHandler = () => void; + +export type Handlers = { + next?: NextHandler; + error?: ErrorHandler; + complete?: CompleteHandler; +}; diff --git a/types/http.ts b/types/http.ts new file mode 100644 index 0000000..fd827ee --- /dev/null +++ b/types/http.ts @@ -0,0 +1,2 @@ +export type HTTP_METHOD = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD'; +export type HTTP_STATUS = 200 | 500; diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..bb4fd9d --- /dev/null +++ b/types/index.ts @@ -0,0 +1,18 @@ +export type { HTTP_METHOD, HTTP_STATUS } from './http'; +export type { UserRole, User } from './user'; +export type { + PostRequest, + GetRequest, + Request, + RequestError, + HandlerResponse, +} from './request'; +export type { + NextHandler, + ErrorHandler, + CompleteHandler, + Handlers, +} from './handlers'; +export type { Unsubscribe, Subscription } from './subscription'; +export type { ObserverLike } from './observer'; +export type { SubscribeFn, ObservableLike, ObservableFactory } from './observable'; diff --git a/types/observable.ts b/types/observable.ts new file mode 100644 index 0000000..fa645dc --- /dev/null +++ b/types/observable.ts @@ -0,0 +1,14 @@ +import type { Handlers } from './handlers'; +import type { ObserverLike } from './observer'; +import type { Request } from './request'; +import type { Subscription, Unsubscribe } from './subscription'; + +export type SubscribeFn = (observer: ObserverLike) => Unsubscribe; + +export type ObservableLike = { + subscribe: (handlers: Handlers) => Subscription; +}; + +export type ObservableFactory = { + from: (values: Request[]) => ObservableLike; +}; diff --git a/types/observer.ts b/types/observer.ts new file mode 100644 index 0000000..e8973a8 --- /dev/null +++ b/types/observer.ts @@ -0,0 +1,7 @@ +import type { Request, RequestError } from './request'; + +export type ObserverLike = { + next: (value: Request) => void; + error: (error: RequestError) => void; + complete: () => void; +}; diff --git a/types/request.ts b/types/request.ts new file mode 100644 index 0000000..88248ef --- /dev/null +++ b/types/request.ts @@ -0,0 +1,28 @@ +import type { HTTP_STATUS } from './http'; +import type { User } from './user'; + +export type PostRequest = { + method: 'POST'; + host: string; + path: string; + body: User; + params?: Record; +}; + +export type GetRequest = { + method: 'GET'; + host: string; + path: string; + params: Record; +}; + +export type Request = PostRequest | GetRequest; + +export type RequestError = { + message: string; + code?: string; +}; + +export type HandlerResponse = { + status: HTTP_STATUS; +}; diff --git a/types/subscription.ts b/types/subscription.ts new file mode 100644 index 0000000..12098fb --- /dev/null +++ b/types/subscription.ts @@ -0,0 +1,5 @@ +export type Unsubscribe = () => void; + +export type Subscription = { + unsubscribe: () => void; +}; diff --git a/types/user.ts b/types/user.ts new file mode 100644 index 0000000..1f74d38 --- /dev/null +++ b/types/user.ts @@ -0,0 +1,9 @@ +export type UserRole = 'user' | 'admin'; + +export type User = { + name: string; + age: number; + roles: UserRole[]; + createdAt: Date; + isDeleated: boolean; +};