Fix image-upload, http-client and config
This commit is contained in:
parent
50d36f268f
commit
69dd12375c
@ -13,14 +13,18 @@ rules:
|
||||
ignorePattern: '^import .* from '
|
||||
comma-dangle:
|
||||
- warn
|
||||
- always-multiline
|
||||
- arrays: always-multiline
|
||||
objects: always-multiline
|
||||
imports: always-multiline
|
||||
exports: always-multiline
|
||||
functions: always-multiline
|
||||
|
||||
# semi:
|
||||
# - warn
|
||||
# - never
|
||||
# interface-name-prefix:
|
||||
'@typescript-eslint/member-delimiter-style':
|
||||
- error
|
||||
- warn
|
||||
- multiline:
|
||||
delimiter: none
|
||||
singleline:
|
||||
|
||||
4
packages/config/.eslintrc.yaml
Normal file
4
packages/config/.eslintrc.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
extends:
|
||||
- ../../.eslintrc.yaml
|
||||
rules:
|
||||
'@typescript-eslint/no-explicit-any': off
|
||||
@ -4,7 +4,7 @@ export class Config {
|
||||
get(key: string) {
|
||||
let value = this.config
|
||||
key.split('.').forEach(k => {
|
||||
if (!value.hasOwnProperty(k)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
throw new Error(`Property "${k}" from "${key}" does not exist`)
|
||||
}
|
||||
value = value[k]
|
||||
@ -15,7 +15,7 @@ export class Config {
|
||||
has(key: string) {
|
||||
let c = this.config
|
||||
return key.split('.').every(k => {
|
||||
const has = c.hasOwnProperty(k)
|
||||
const has = Object.prototype.hasOwnProperty.call(c, k)
|
||||
if (has) {
|
||||
c = c[k]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {ConfigReader} from './ConfigReader'
|
||||
import {join} from 'path'
|
||||
import {writeFileSync} from 'fs'
|
||||
import { writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { ConfigReader } from './ConfigReader'
|
||||
|
||||
describe('ConfigReader', () => {
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import loggerFactory, { Logger } from '@rondo.dev/logger'
|
||||
import { readFileSync } from 'fs'
|
||||
import YAML from 'js-yaml'
|
||||
import { join } from 'path'
|
||||
import { Config } from './Config'
|
||||
import { findPackageRoot } from './findPackageRoot'
|
||||
import loggerFactory, {ILogger} from '@rondo.dev/logger'
|
||||
|
||||
const isObject = (value: any) => value !== null && typeof value === 'object'
|
||||
const isObject = (value: unknown) => value !== null && typeof value === 'object'
|
||||
|
||||
export class ConfigReader {
|
||||
protected readonly config: any = {}
|
||||
@ -16,7 +16,7 @@ export class ConfigReader {
|
||||
readonly path: string,
|
||||
readonly cwd: string | undefined = process.cwd(),
|
||||
readonly environment = 'CONFIG',
|
||||
readonly logger: ILogger = loggerFactory.getLogger('config'),
|
||||
readonly logger: Logger = loggerFactory.getLogger('config'),
|
||||
) {
|
||||
const packageRoot = path && findPackageRoot(path)
|
||||
this.locations = packageRoot ? [packageRoot] : []
|
||||
@ -93,7 +93,8 @@ export class ConfigReader {
|
||||
// }
|
||||
const value = src[key]
|
||||
if (isObject(value) && !Array.isArray(value)) {
|
||||
if (!dest.hasOwnProperty(key) ||
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(dest, key) ||
|
||||
Array.isArray(dest[key]) ||
|
||||
!isObject(dest[key])
|
||||
) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {resolve, join} from 'path'
|
||||
import {statSync, Stats} from 'fs'
|
||||
import { Stats, statSync } from 'fs'
|
||||
import { join, resolve } from 'path'
|
||||
|
||||
function findNearestDirectory(
|
||||
dir: string, filename: string,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export * from './Config'
|
||||
|
||||
import {ConfigReader} from './ConfigReader'
|
||||
import { ConfigReader } from './ConfigReader'
|
||||
export default ConfigReader
|
||||
|
||||
4
packages/http-client/.eslintrc.yaml
Normal file
4
packages/http-client/.eslintrc.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
extends:
|
||||
- ../../.eslintrc.yaml
|
||||
rules:
|
||||
'@typescript-eslint/no-explicit-any': off
|
||||
@ -1,140 +1,51 @@
|
||||
import axios from 'axios'
|
||||
import {IHTTPClient} from './IHTTPClient'
|
||||
import {IHeader} from './IHeader'
|
||||
import {TMethod, IRoutes} from '@rondo.dev/http-types'
|
||||
import {URLFormatter} from './URLFormatter'
|
||||
import {IRequest} from './IRequest'
|
||||
import {IResponse} from './IResponse'
|
||||
import {ITypedRequestParams} from './ITypedRequestParams'
|
||||
import {Method, Routes} from '@rondo.dev/http-types'
|
||||
import {TypedRequestParams} from './TypedRequestParams'
|
||||
|
||||
interface IRequestor {
|
||||
request: (params: IRequest) => Promise<IResponse>
|
||||
}
|
||||
|
||||
export class HTTPClient<T extends IRoutes> implements IHTTPClient<T> {
|
||||
protected readonly requestor: IRequestor
|
||||
protected readonly formatter: URLFormatter
|
||||
|
||||
constructor(
|
||||
protected readonly baseURL = '',
|
||||
protected readonly headers?: IHeader,
|
||||
) {
|
||||
this.requestor = this.createRequestor()
|
||||
this.formatter = new URLFormatter()
|
||||
}
|
||||
|
||||
protected createRequestor(): IRequestor {
|
||||
return axios.create({
|
||||
baseURL: this.baseURL,
|
||||
headers: this.headers,
|
||||
})
|
||||
}
|
||||
|
||||
async request<
|
||||
export interface HTTPClient<T extends Routes> {
|
||||
request<
|
||||
P extends keyof T & string,
|
||||
M extends TMethod,
|
||||
>(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']> {
|
||||
|
||||
const url = this.formatter.format(params.path, params.params)
|
||||
|
||||
const response = await this.requestor.request({
|
||||
method: params.method,
|
||||
url,
|
||||
params: params.query,
|
||||
data: params.body,
|
||||
})
|
||||
|
||||
return response.data
|
||||
}
|
||||
M extends Method,
|
||||
>(params: TypedRequestParams<T, P, M>): Promise<T[P][M]['response']>
|
||||
|
||||
get<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['get']['query'],
|
||||
params?: T[P]['get']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'get',
|
||||
path,
|
||||
query,
|
||||
params,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['get']['response']>
|
||||
|
||||
post<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['post']['body'],
|
||||
params?: T[P]['post']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'post',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['post']['response']>
|
||||
|
||||
put<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['put']['body'],
|
||||
params?: T[P]['put']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'put',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['put']['response']>
|
||||
|
||||
delete<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['delete']['body'],
|
||||
params?: T[P]['delete']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'delete',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['delete']['response']>
|
||||
|
||||
head<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['head']['query'],
|
||||
params?: T[P]['head']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'head',
|
||||
path,
|
||||
params,
|
||||
query,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['head']['response']>
|
||||
|
||||
options<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['options']['query'],
|
||||
params?: T[P]['options']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'options',
|
||||
path,
|
||||
params,
|
||||
query,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['options']['response']>
|
||||
|
||||
patch<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['patch']['body'],
|
||||
params?: T[P]['patch']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'patch',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
): Promise<T[P]['patch']['response']>
|
||||
}
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
import {HTTPClient} from './HTTPClient'
|
||||
import {IRequest} from './IRequest'
|
||||
import {IResponse} from './IResponse'
|
||||
import {IRoutes, TMethod} from '@rondo.dev/http-types'
|
||||
import {ITypedRequestParams} from './ITypedRequestParams'
|
||||
import {SimpleHTTPClient} from './SimpleHTTPClient'
|
||||
import {Request} from './Request'
|
||||
import {Response} from './Response'
|
||||
import {Routes, Method} from '@rondo.dev/http-types'
|
||||
import {TypedRequestParams} from './TypedRequestParams'
|
||||
|
||||
interface IReqRes {
|
||||
req: IRequest
|
||||
res: IResponse
|
||||
interface ReqRes {
|
||||
req: Request
|
||||
res: Response
|
||||
}
|
||||
|
||||
export class HTTPClientError extends Error {
|
||||
constructor(readonly request: IRequest, readonly response: IResponse) {
|
||||
constructor(readonly request: Request, readonly response: Response) {
|
||||
super('HTTP Status: ' + response.status)
|
||||
Error.captureStackTrace(this)
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRequestStatus {
|
||||
request: IRequest
|
||||
export interface RequestStatus {
|
||||
request: Request
|
||||
finished: boolean
|
||||
}
|
||||
|
||||
export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
mocks: {[key: string]: IResponse} = {}
|
||||
requests: IRequestStatus[] = []
|
||||
export class HTTPClientMock<T extends Routes> extends SimpleHTTPClient<T> {
|
||||
mocks: {[key: string]: Response} = {}
|
||||
requests: RequestStatus[] = []
|
||||
|
||||
protected waitPromise?: {
|
||||
resolve: (r: IReqRes) => void
|
||||
resolve: (r: ReqRes) => void
|
||||
reject: (err: Error) => void
|
||||
}
|
||||
|
||||
@ -39,15 +39,15 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
*/
|
||||
createRequestor() {
|
||||
return {
|
||||
request: (req: IRequest): Promise<IResponse> => {
|
||||
const currentRequest: IRequestStatus = {
|
||||
request: (req: Request): Promise<Response> => {
|
||||
const currentRequest: RequestStatus = {
|
||||
request: req,
|
||||
finished: false,
|
||||
}
|
||||
this.requests.push(currentRequest)
|
||||
return new Promise((resolve, reject) => {
|
||||
const key = this.serialize(req)
|
||||
if (!this.mocks.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.mocks, key)) {
|
||||
setImmediate(() => {
|
||||
const err = new Error(
|
||||
'No mock for request: ' + key + '\nAvailable mocks: ' +
|
||||
@ -76,7 +76,7 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected serialize(req: IRequest) {
|
||||
protected serialize(req: Request) {
|
||||
return JSON.stringify({
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
@ -90,7 +90,7 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
* replaced. The signature is calculated using the `serialize()` method,
|
||||
* which just does a `JSON.stringify(req)`.
|
||||
*/
|
||||
mockAdd(req: IRequest, data: any, status = 200): this {
|
||||
mockAdd(req: Request, data: any, status = 200): this {
|
||||
this.mocks[this.serialize(req)] = {data, status}
|
||||
return this
|
||||
}
|
||||
@ -98,8 +98,8 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
/**
|
||||
* Adds a new mock with predefined type
|
||||
*/
|
||||
mockAddTyped<P extends keyof T & string, M extends TMethod>(
|
||||
params: ITypedRequestParams<T, P, M>,
|
||||
mockAddTyped<P extends keyof T & string, M extends Method>(
|
||||
params: TypedRequestParams<T, P, M>,
|
||||
response: T[P][M]['response'],
|
||||
): this {
|
||||
const url = this.formatter.format(params.path, params.params)
|
||||
@ -120,7 +120,7 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
return this
|
||||
}
|
||||
|
||||
protected notify(r: IReqRes | Error) {
|
||||
protected notify(r: ReqRes | Error) {
|
||||
if (!this.waitPromise) {
|
||||
return
|
||||
}
|
||||
@ -147,12 +147,12 @@ export class HTTPClientMock<T extends IRoutes> extends HTTPClient<T> {
|
||||
* const {req, res} = await httpMock.wait()
|
||||
* expect(req).toEqual({method:'get', url:'/auth/post', data: {...}})
|
||||
*/
|
||||
async wait(): Promise<IReqRes> {
|
||||
async wait(): Promise<ReqRes> {
|
||||
if (this.requests.every(r => r.finished)) {
|
||||
throw new Error('No requests to wait for')
|
||||
}
|
||||
expect(this.waitPromise).toBe(undefined)
|
||||
const result: IReqRes = await new Promise((resolve, reject) => {
|
||||
const result: ReqRes = await new Promise((resolve, reject) => {
|
||||
this.waitPromise = {resolve, reject}
|
||||
})
|
||||
// TODO think of a better way to do this.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export interface IHeader {
|
||||
export interface Headers {
|
||||
readonly [key: string]: string
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
import {TMethod, IRoutes} from '@rondo.dev/http-types'
|
||||
import {ITypedRequestParams} from './ITypedRequestParams'
|
||||
|
||||
export interface IHTTPClient<T extends IRoutes> {
|
||||
request<
|
||||
P extends keyof T & string,
|
||||
M extends TMethod,
|
||||
>(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']>
|
||||
|
||||
get<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['get']['query'],
|
||||
params?: T[P]['get']['params'],
|
||||
): Promise<T[P]['get']['response']>
|
||||
|
||||
post<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['post']['body'],
|
||||
params?: T[P]['post']['params'],
|
||||
): Promise<T[P]['post']['response']>
|
||||
|
||||
put<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['put']['body'],
|
||||
params?: T[P]['put']['params'],
|
||||
): Promise<T[P]['put']['response']>
|
||||
|
||||
delete<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['delete']['body'],
|
||||
params?: T[P]['delete']['params'],
|
||||
): Promise<T[P]['delete']['response']>
|
||||
|
||||
head<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['head']['query'],
|
||||
params?: T[P]['head']['params'],
|
||||
): Promise<T[P]['head']['response']>
|
||||
|
||||
options<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['options']['query'],
|
||||
params?: T[P]['options']['params'],
|
||||
): Promise<T[P]['options']['response']>
|
||||
|
||||
patch<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['patch']['body'],
|
||||
params?: T[P]['patch']['params'],
|
||||
): Promise<T[P]['patch']['response']>
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import {TMethod} from '@rondo.dev/http-types'
|
||||
|
||||
export interface IRequest {
|
||||
method: TMethod,
|
||||
url: string,
|
||||
params?: {[key: string]: any},
|
||||
data?: any,
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import {IRoutes, TMethod} from '@rondo.dev/http-types'
|
||||
|
||||
export interface ITypedRequestParams<
|
||||
T extends IRoutes,
|
||||
P extends keyof T & string,
|
||||
M extends TMethod,
|
||||
> {
|
||||
method: M,
|
||||
path: P,
|
||||
params?: T[P][M]['params'],
|
||||
query?: T[P][M]['query'],
|
||||
body?: T[P][M]['body'],
|
||||
}
|
||||
8
packages/http-client/src/Request.ts
Normal file
8
packages/http-client/src/Request.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import {Method} from '@rondo.dev/http-types'
|
||||
|
||||
export interface Request {
|
||||
method: Method
|
||||
url: string
|
||||
params?: {[key: string]: any}
|
||||
data?: any
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
export interface IRequestQuery {
|
||||
export interface RequestParams {
|
||||
[key: string]: string | number
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
export interface IRequestParams {
|
||||
export interface RequestQuery {
|
||||
[key: string]: string | number
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export interface IResponse {
|
||||
export interface Response {
|
||||
data: any
|
||||
status: number
|
||||
}
|
||||
140
packages/http-client/src/SimpleHTTPClient.ts
Normal file
140
packages/http-client/src/SimpleHTTPClient.ts
Normal file
@ -0,0 +1,140 @@
|
||||
import axios from 'axios'
|
||||
import {HTTPClient} from './HTTPClient'
|
||||
import {Headers} from './Headers'
|
||||
import {Method, Routes} from '@rondo.dev/http-types'
|
||||
import {URLFormatter} from './URLFormatter'
|
||||
import {Request} from './Request'
|
||||
import {Response} from './Response'
|
||||
import {TypedRequestParams} from './TypedRequestParams'
|
||||
|
||||
interface Requestor {
|
||||
request: (params: Request) => Promise<Response>
|
||||
}
|
||||
|
||||
export class SimpleHTTPClient<T extends Routes> implements HTTPClient<T> {
|
||||
protected readonly requestor: Requestor
|
||||
protected readonly formatter: URLFormatter
|
||||
|
||||
constructor(
|
||||
protected readonly baseURL = '',
|
||||
protected readonly headers?: Headers,
|
||||
) {
|
||||
this.requestor = this.createRequestor()
|
||||
this.formatter = new URLFormatter()
|
||||
}
|
||||
|
||||
protected createRequestor(): Requestor {
|
||||
return axios.create({
|
||||
baseURL: this.baseURL,
|
||||
headers: this.headers,
|
||||
})
|
||||
}
|
||||
|
||||
async request<
|
||||
P extends keyof T & string,
|
||||
M extends Method,
|
||||
>(params: TypedRequestParams<T, P, M>): Promise<T[P][M]['response']> {
|
||||
|
||||
const url = this.formatter.format(params.path, params.params)
|
||||
|
||||
const response = await this.requestor.request({
|
||||
method: params.method,
|
||||
url,
|
||||
params: params.query,
|
||||
data: params.body,
|
||||
})
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
get<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['get']['query'],
|
||||
params?: T[P]['get']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'get',
|
||||
path,
|
||||
query,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
post<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['post']['body'],
|
||||
params?: T[P]['post']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'post',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
put<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['put']['body'],
|
||||
params?: T[P]['put']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'put',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
delete<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['delete']['body'],
|
||||
params?: T[P]['delete']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'delete',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
head<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['head']['query'],
|
||||
params?: T[P]['head']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'head',
|
||||
path,
|
||||
params,
|
||||
query,
|
||||
})
|
||||
}
|
||||
|
||||
options<P extends keyof T & string>(
|
||||
path: P,
|
||||
query?: T[P]['options']['query'],
|
||||
params?: T[P]['options']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'options',
|
||||
path,
|
||||
params,
|
||||
query,
|
||||
})
|
||||
}
|
||||
|
||||
patch<P extends keyof T & string>(
|
||||
path: P,
|
||||
body: T[P]['patch']['body'],
|
||||
params?: T[P]['patch']['params'],
|
||||
) {
|
||||
return this.request({
|
||||
method: 'patch',
|
||||
path,
|
||||
body,
|
||||
params,
|
||||
})
|
||||
}
|
||||
}
|
||||
13
packages/http-client/src/TypedRequestParams.ts
Normal file
13
packages/http-client/src/TypedRequestParams.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {Routes, Method} from '@rondo.dev/http-types'
|
||||
|
||||
export interface TypedRequestParams<
|
||||
T extends Routes,
|
||||
P extends keyof T & string,
|
||||
M extends Method,
|
||||
> {
|
||||
method: M
|
||||
path: P
|
||||
params?: T[P][M]['params']
|
||||
query?: T[P][M]['query']
|
||||
body?: T[P][M]['body']
|
||||
}
|
||||
@ -1,27 +1,27 @@
|
||||
import {IRequestParams} from './IRequestParams'
|
||||
import {IRequestQuery} from './IRequestQuery'
|
||||
import {RequestParams} from './RequestParams'
|
||||
import {RequestQuery} from './RequestQuery'
|
||||
|
||||
export interface IURLFormatterOptions {
|
||||
export interface URLFormatterOptions {
|
||||
readonly baseURL: string
|
||||
readonly regex: RegExp
|
||||
}
|
||||
|
||||
export class URLFormatter {
|
||||
constructor(readonly params: IURLFormatterOptions = {
|
||||
constructor(readonly params: URLFormatterOptions = {
|
||||
baseURL: '',
|
||||
regex: /:[a-zA-Z0-9-]+/g,
|
||||
}) {}
|
||||
|
||||
format(
|
||||
url: string,
|
||||
params?: IRequestParams,
|
||||
query?: IRequestQuery,
|
||||
params?: RequestParams,
|
||||
query?: RequestQuery,
|
||||
) {
|
||||
let formattedUrl = url
|
||||
if (params) {
|
||||
formattedUrl = url.replace(this.params.regex, match => {
|
||||
const key = match.substring(1)
|
||||
if (!params.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
||||
throw new Error('Undefined URL paramter: ' + key)
|
||||
}
|
||||
return String(params![key])
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export * from './HTTPClient'
|
||||
export * from './HTTPClientMock'
|
||||
export * from './IHeader'
|
||||
export * from './IHTTPClient'
|
||||
export * from './IRequest'
|
||||
export * from './IRequestParams'
|
||||
export * from './IRequestQuery'
|
||||
export * from './IResponse'
|
||||
export * from './ITypedRequestParams'
|
||||
export * from './Headers'
|
||||
export * from './HTTPClient'
|
||||
export * from './Request'
|
||||
export * from './RequestParams'
|
||||
export * from './RequestQuery'
|
||||
export * from './Response'
|
||||
export * from './TypedRequestParams'
|
||||
export * from './URLFormatter'
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export type TMethod = 'get'
|
||||
/* eslint @typescript-eslint/no-explicit-any: 0 */
|
||||
|
||||
export type Method = 'get'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'delete'
|
||||
@ -6,14 +8,14 @@ export type TMethod = 'get'
|
||||
| 'head'
|
||||
| 'options'
|
||||
|
||||
export interface IRoutes {
|
||||
export interface Routes {
|
||||
// has to be any because otherwise TypeScript will start
|
||||
// throwing error and interfaces without an index signature
|
||||
// would not be usable
|
||||
[route: string]: any
|
||||
}
|
||||
|
||||
export interface IRoute {
|
||||
export interface Route {
|
||||
params: any
|
||||
query: any
|
||||
body: any
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
|
||||
function attachErrorListener(
|
||||
reader: FileReader,
|
||||
file: File,
|
||||
reject: (err: Error) => void,
|
||||
) {
|
||||
reader.onerror = ev => reject(new Error('Error reading file: ' +
|
||||
reader.onerror = () => reject(new Error('Error reading file: ' +
|
||||
(reader.error ? reader.error.message : file.name)))
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export async function createImageFromDataURL(dataURL: string)
|
||||
: Promise<HTMLImageElement> {
|
||||
export async function createImageFromDataURL(
|
||||
dataURL: string,
|
||||
): Promise<HTMLImageElement> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const image = new Image()
|
||||
image.onload = () => resolve(image)
|
||||
@ -8,8 +9,9 @@ export async function createImageFromDataURL(dataURL: string)
|
||||
})
|
||||
}
|
||||
|
||||
export async function drawCanvasFromDataURL(dataURL: string)
|
||||
: Promise<HTMLCanvasElement> {
|
||||
export async function drawCanvasFromDataURL(
|
||||
dataURL: string,
|
||||
): Promise<HTMLCanvasElement> {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')!
|
||||
const image = await createImageFromDataURL(dataURL)
|
||||
@ -24,7 +26,7 @@ export async function getCanvasFromArrayBuffer(
|
||||
width: number,
|
||||
height: number,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise(() => {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')!
|
||||
const imageData = new ImageData(
|
||||
|
||||
@ -3,34 +3,33 @@ import {Resizer} from './Resizer'
|
||||
import {readAsDataURL} from './Files'
|
||||
import {drawCanvasFromDataURL} from './Image'
|
||||
|
||||
export interface IImage {
|
||||
export interface Image {
|
||||
dataURL: string
|
||||
}
|
||||
|
||||
export interface IImageUploadProps {
|
||||
onChange: (images: IImage[]) => void
|
||||
export interface ImageUploadProps {
|
||||
onChange: (images: Image[]) => void
|
||||
multiple: boolean
|
||||
}
|
||||
|
||||
export class ImageUpload extends React.PureComponent<IImageUploadProps> {
|
||||
export class ImageUpload extends React.PureComponent<ImageUploadProps> {
|
||||
fileInput: React.RefObject<HTMLInputElement>
|
||||
constructor(props: IImageUploadProps) {
|
||||
constructor(props: ImageUploadProps) {
|
||||
super(props)
|
||||
this.fileInput = React.createRef()
|
||||
}
|
||||
|
||||
safeHandleChange = async (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
safeHandleChange = async () => {
|
||||
try {
|
||||
await this.handleChange(event)
|
||||
await this.handleChange()
|
||||
} catch (err) {
|
||||
// console.log('Error in handleChange', err)
|
||||
}
|
||||
}
|
||||
handleChange = async (event: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const self = this
|
||||
handleChange = async () => {
|
||||
const files = Array.from(this.fileInput.current!.files!)
|
||||
|
||||
const resized: IImage[] = []
|
||||
const resized: Image[] = []
|
||||
for (const file of files) {
|
||||
const dataURL = await readAsDataURL(file)
|
||||
const resizedDataURL = await this.resize(dataURL)
|
||||
@ -44,7 +43,7 @@ export class ImageUpload extends React.PureComponent<IImageUploadProps> {
|
||||
this.fileInput.current!.parentElement!.appendChild(img)
|
||||
}
|
||||
|
||||
(window as any).resized = resized
|
||||
// (window as any).resized = resized
|
||||
this.props.onChange(resized)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
interface IWorkerParam {
|
||||
interface WorkerParam {
|
||||
sourceWidth: number
|
||||
sourceHeight: number
|
||||
width: number
|
||||
@ -6,21 +6,22 @@ interface IWorkerParam {
|
||||
source: ArrayBuffer
|
||||
}
|
||||
|
||||
interface IWorkerParamMessage {
|
||||
data: IWorkerParam
|
||||
interface WorkerParamMessage {
|
||||
data: WorkerParam
|
||||
}
|
||||
|
||||
interface IWorkerResult {
|
||||
interface WorkerResult {
|
||||
target: Uint8ClampedArray
|
||||
}
|
||||
|
||||
interface IWorkerResultMessage {
|
||||
data: IWorkerResult
|
||||
interface WorkerResultMessage {
|
||||
data: WorkerResult
|
||||
}
|
||||
|
||||
function createResizeWorker(root: any = {})
|
||||
: {onmessage: (event: IWorkerParamMessage) => void} {
|
||||
root.onmessage = (event: IWorkerParamMessage) => {
|
||||
function createResizeWorker(
|
||||
root: any = {}, // eslint-disable-line
|
||||
): {onmessage: (event: WorkerParamMessage) => void} {
|
||||
root.onmessage = (event: WorkerParamMessage) => {
|
||||
const sourceWidth = event.data.sourceWidth
|
||||
const sourceHeight = event.data.sourceHeight
|
||||
const width = event.data.width
|
||||
@ -32,7 +33,7 @@ function createResizeWorker(root: any = {})
|
||||
const ratioHalfH = Math.ceil(ratioH / 2)
|
||||
|
||||
const source = new Uint8ClampedArray(event.data.source)
|
||||
const sourceH = source.length / sourceWidth / 4
|
||||
// const sourceH = source.length / sourceWidth / 4
|
||||
const targetSize = width * height * 4
|
||||
const targetMemory = new ArrayBuffer(targetSize)
|
||||
const target = new Uint8ClampedArray(targetMemory, 0, targetSize)
|
||||
@ -92,10 +93,10 @@ function createResizeWorker(root: any = {})
|
||||
}
|
||||
}
|
||||
|
||||
const objData: IWorkerResult = {
|
||||
const objData: WorkerResult = {
|
||||
target,
|
||||
}
|
||||
postMessage(objData, [target.buffer] as any)
|
||||
postMessage(objData, [target.buffer] as any) // eslint-disable-line
|
||||
}
|
||||
return root
|
||||
}
|
||||
@ -156,7 +157,7 @@ export class Resizer {
|
||||
const worker = new Worker(this.workerBlobURL)
|
||||
activeWorkers += 1
|
||||
workers[c] = worker
|
||||
worker.onmessage = (event: IWorkerResultMessage) => {
|
||||
worker.onmessage = (event: WorkerResultMessage) => {
|
||||
worker.terminate()
|
||||
delete workers[c]
|
||||
activeWorkers -= 1
|
||||
@ -174,7 +175,7 @@ export class Resizer {
|
||||
reject(new Error('Error resizing: ' + err.message))
|
||||
}
|
||||
|
||||
const message: IWorkerParam = {
|
||||
const message: WorkerParam = {
|
||||
sourceWidth,
|
||||
sourceHeight: partition.height,
|
||||
width,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user