Browse Source

Migrate RemoteStorage to TS

pull/4830/head
Artur Arseniev 4 years ago
parent
commit
fe06d92073
  1. 59
      src/storage_manager/config/config.ts
  2. 8
      src/storage_manager/model/IStorage.ts
  3. 66
      src/storage_manager/model/RemoteStorage.js
  4. 127
      src/storage_manager/model/RemoteStorage.ts

59
src/storage_manager/config/config.ts

@ -1,63 +1,6 @@
import Editor from '../../editor'; import Editor from '../../editor';
import { LocalStorageConfig } from '../model/LocalStorage'; import { LocalStorageConfig } from '../model/LocalStorage';
import { RemoteStorageConfig } from '../model/RemoteStorage';
type AnyObject = Record<string, any>;
export interface RemoteStorageConfig {
/**
* Custom headers.
* @default {}
*/
headers?: AnyObject;
/**
* Endpoint URL where to store data project.
*/
urlStore?: string;
/**
* Endpoint URL where to load data project.
*/
urlLoad?: string;
/**
* Use JSON contentType.
* @default true
*/
contentTypeJson?: boolean;
/**
* Credentials option for the fetch API.
* @default 'include'
*/
credentials?: RequestCredentials;
/**
* Pass custom options to fetch API (remote storage)
* You can pass a simple object: { someOption: 'someValue' }
* or a function which returns and object to add:
* @example
* fetchOptions: currentOpts => {
* return currentOpts.method === 'POST' ? { method: 'PATCH' } : {};
* },
*/
fetchOptions?: string | ((opts: AnyObject) => object);
/**
* The remote storage sends the project data as a body of the request.
* You can use this method to update the body before the store call in order to align
* with your API requirements.
* @default data => data
*/
onStore?: (data: AnyObject, editor: Editor) => AnyObject;
/**
* The remote storage loads the project data directly from the request response.
* You can use this method to properly extract the project data from the response.
* @default data => data
*/
onLoad?: (data: AnyObject, editor: Editor) => AnyObject;
}
export interface StorageManagerConfig { export interface StorageManagerConfig {
/** /**

8
src/storage_manager/model/IStorage.ts

@ -1,6 +1,10 @@
export interface StorageOptions {} export interface StorageOptions {
[key: string]: any;
}
export interface ProjectData {} export interface ProjectData {
[key: string]: any;
}
export default interface IStorage<T extends StorageOptions = {}> { export default interface IStorage<T extends StorageOptions = {}> {
load: (options?: T) => Promise<ProjectData>; load: (options?: T) => Promise<ProjectData>;

66
src/storage_manager/model/RemoteStorage.js

@ -1,66 +0,0 @@
import { isUndefined, isFunction, isString } from 'underscore';
import fetch from '../../utils/fetch';
export default class RemoteStorage {
async store(data, opts = {}) {
await this.request(opts.urlStore, this.__props(opts, data), opts);
}
async load(opts) {
return await this.request(opts.urlLoad, this.__props(opts), opts);
}
request(url, props = {}, opts = {}) {
return fetch(url, props)
.then(res => {
const result = res.text();
const isOk = ((res.status / 200) | 0) === 1;
return isOk ? result : result.then(Promise.reject);
})
.then(text => {
const parsable = text && isString(text);
return opts.contentTypeJson && parsable ? JSON.parse(text) : text;
});
}
__props(opts = {}, data) {
const typeJson = opts.contentTypeJson;
const headers = opts.headers || {};
const fetchOpts = opts.fetchOptions || {};
const reqHead = 'X-Requested-With';
const typeHead = 'Content-Type';
let body;
if (isUndefined(headers[reqHead])) {
headers[reqHead] = 'XMLHttpRequest';
}
if (isUndefined(headers[typeHead]) && typeJson) {
headers[typeHead] = 'application/json; charset=utf-8';
}
if (data) {
if (typeJson) {
body = JSON.stringify(data);
} else {
body = new FormData();
for (let key in data) {
body.append(key, data[key]);
}
}
}
const result = {
method: body ? 'POST' : 'GET',
credentials: opts.credentials,
headers,
body,
};
return {
...result,
...(isFunction(fetchOpts) ? fetchOpts(result) : fetchOpts),
};
}
}

127
src/storage_manager/model/RemoteStorage.ts

@ -0,0 +1,127 @@
import Editor from '../../editor';
import { isUndefined, isFunction, isString } from 'underscore';
import fetch from '../../utils/fetch';
import IStorage, { ProjectData } from './IStorage';
import { ModelFetchOptions } from 'backbone';
type AnyObject = Record<string, any>;
export interface RemoteStorageConfig {
/**
* Custom headers.
* @default {}
*/
headers?: AnyObject;
/**
* Endpoint URL where to store data project.
*/
urlStore?: string;
/**
* Endpoint URL where to load data project.
*/
urlLoad?: string;
/**
* Use JSON contentType.
* @default true
*/
contentTypeJson?: boolean;
/**
* Credentials option for the fetch API.
* @default 'include'
*/
credentials?: RequestCredentials;
/**
* Pass custom options to fetch API (remote storage)
* You can pass a simple object: { someOption: 'someValue' }
* or a function which returns and object to add:
* @example
* fetchOptions: currentOpts => {
* return currentOpts.method === 'POST' ? { method: 'PATCH' } : {};
* },
*/
fetchOptions?: string | ((opts: AnyObject) => object);
/**
* The remote storage sends the project data as a body of the request.
* You can use this method to update the body before the store call in order to align
* with your API requirements.
* @default data => data
*/
onStore?: (data: AnyObject, editor: Editor) => AnyObject;
/**
* The remote storage loads the project data directly from the request response.
* You can use this method to properly extract the project data from the response.
* @default data => data
*/
onLoad?: (data: AnyObject, editor: Editor) => AnyObject;
}
export default class RemoteStorage implements IStorage<RemoteStorageConfig> {
async store(data: ProjectData, opts: RemoteStorageConfig = {}) {
await this.request(opts.urlStore!, this.__props(opts, data), opts);
}
async load(opts: RemoteStorageConfig = {}) {
return await this.request(opts.urlLoad!, this.__props(opts), opts);
}
request(url: string, props: RequestInit = {}, opts: RemoteStorageConfig = {}) {
return fetch(url, props)
.then((res: any) => {
const result = res.text();
const isOk = ((res.status / 200) | 0) === 1;
return isOk ? result : result.then(Promise.reject);
})
.then(text => {
const parsable = text && isString(text);
return opts.contentTypeJson && parsable ? JSON.parse(text) : text;
});
}
__props(opts: RemoteStorageConfig = {}, data?: ProjectData): RequestInit {
const typeJson = opts.contentTypeJson;
const headers = opts.headers || {};
const fetchOpts = opts.fetchOptions || {};
const reqHead = 'X-Requested-With';
const typeHead = 'Content-Type';
let body;
if (isUndefined(headers[reqHead])) {
headers[reqHead] = 'XMLHttpRequest';
}
if (isUndefined(headers[typeHead]) && typeJson) {
headers[typeHead] = 'application/json; charset=utf-8';
}
if (data) {
if (typeJson) {
body = JSON.stringify(data);
} else {
body = new FormData();
for (let key in data) {
body.append(key, data[key]);
}
}
}
const result = {
method: body ? 'POST' : 'GET',
credentials: opts.credentials,
headers,
body,
};
return {
...result,
...(isFunction(fetchOpts) ? fetchOpts(result) : fetchOpts),
};
}
}
Loading…
Cancel
Save