mushroomjs
v1.2.0
Published
Mushroom js driver
Readme
Install
npm install mushroomjsor
yarn add mushroomjsImport
Simple
import mushroom from "mushroomjs";Builder
import { Filter, Sort, Project } from 'mushroomjs';Full
import mushroom, {
defineAsyncResource,
defineAsyncView,
fireEvent,
createRestfulAsyncFunction,
IdType,
IMushroom,
Mushroom,
MushroomRequest,
MushroomRequestSetting,
MushroomRestfulRequest,
MushroomResponse,
MushroomResourceBase,
MushroomListResource,
MushroomFindByIdResource,
MushroomCreateResource,
MushroomBatchCreateResource,
MushroomUpdateResource,
MushroomBatchUpdateResource,
MushroomPartialUpdateResource,
MushroomDeleteResource,
MushroomBatchDeleteResource,
MushroomExtensibleResource,
MushroomError
} from 'mushroomjs';Define resource
Defination
Create a typescript defination script. Ex: api.ts
import mushroom, {
defineAsyncResource,
IMushroom,
MushroomRequest,
MushroomRequestSetting,
MushroomResourceBase,
MushroomListResource,
MushroomCountResource,
MushroomFindByIdResource,
MushroomCreateResource,
MushroomBatchCreateResource,
MushroomUpdateResource,
MushroomBatchUpdateResource,
MushroomPartialUpdateResource,
MushroomDeleteResource,
MushroomBatchDeleteResource,
MushroomListResponse,
MushroomCountResponse
} from "mushroomjs";
interface Foo {
id?: string,
x?: number,
y?: boolean
}
interface FooCustomFunctionRequest extends MushroomRequest {
params: {
f1: boolean,
f2?: string
},
body: {
a: string,
b?: number
}
}
interface FooCustomFunctionResult {
u: string,
v: number
}
interface FooCustomFunction {
customFunctionAsync(request: FooCustomFunctionRequest): Promise<MushroomResponse<FooCustomFunctionResult>>
}
interface FooViewSampleParameters {
param1: string,
param2?: number;
}
interface FooViewSampleItemResult {
v1: IdType,
v2: boolean,
v3?: number
}
interface FooViewSample {
views: {
sampleAsync(viewParams: FooViewSampleParameters, settings?: MushroomRequestSetting): Promise<MushroomResponse<FooViewSampleItemResult[]>>
}
}
defineAsyncResource<Foo>({
name: "foo",
actions: {
findMany: {
clientCache: false, // false means disabled cache
paging: "limitOffset",
includeTotal: true
},
findById: {
clientCache: true // false means disabled cache
},
createOne: {},
createMany: {},
updateOne: {},
updateMany: {},
updatePartially: {},
deleteOne: {},
deleteMany: {},
_raw_http_method_customFunction: {}
},
views: {
sample: { clientCache: true }
}
});
mushroom.$using("your absolute root API URL");
interface MushroomApi extends IMushroom {
foo: MushroomResourceBase & MushroomListResource<Foo> & MushroomCountResource, & MushroomFindByIdResource<Foo> & MushroomCreateResource<Foo> & MushroomBatchCreateResource<Foo> & MushroomUpdateResource<Foo> & MushroomBatchUpdateResource<Foo> & MushroomPartialUpdateResource<Foo> & MushroomDeleteResource & MushroomBatchDeleteResource & FooCustomFunction & FooViewSample
}
export default mushroom as MushroomApi;Usage
import mushroom from "./api"
async function example() {
await mushroom.foo.listAsync();
await mushroom.foo.getAllAsync();
await mushroom.foo.countAsync();
await mushroom.foo.findByIdAsync({id: yourId});
let newId = await mushroom.foo.createAsync(fooInstance);
let newIds = await mushroom.foo.batchCreateAsync(fooInstances);
await mushroom.foo.updateAsync(fooInstance);
await mushroom.foo.batchUpdateAsync(fooInstances);
await mushroom.foo.partialUpdateAsync(fooInstance);
await mushroom.foo.deleteAsync(id);
await mushroom.foo.batchDeleteAsync(ids);
await muuhroom.foo.customFunction({ params: {f1: true}, body: {a: 10} });
let result = await mushroom.foo.views.sample({param1: "abc", param2: 1});
}Customize headers or/and params of common action
mushroom.foo.createAsync(fooInstance, {
extra: {
headers: {
// your custom headers
},
params: {
// your custom params
}
}
});[Advanced] Call as prefer role
To specify the role of user, use field preferRole of second parameter. Eg:
The current user has 2 roles Admin and User, api foo.list supports both roles and Admin is in higher priority, to switch to User role, please follow this example:
mushroom.foo.listAsync(arg, {
preferRole: 'User'
});[Advanced] Raw request body for customized API
In some special APIs, request data is required in other than JSON format. In this case, use isRawData to tell mushroomjs. Eg:
// Request body in BINARY format
mushroom.foo.specialApiAsync({
body: file, // File
settings: {
isRawData: true,
extra: {
headers: {
'Content-Type': 'image/jpeg' // change to the MIME type of your file or use octet/stream for general case
}
}
}
});
// Request body in FORMDATA format
mushroom.foo.specialApiAsync({
body: formdata, // FormData
settings: {
isRawData: true
}
});[Advanced] Text response and Blob response
In some special APIs, response data is not in JSON format. In this case, please do not use response.result, use response.resultAsText(): string or response.resultAsBlob(): Blob instead. Ex:
const response = await mushroom.foo.specialApiAsync(/*parameters here if any*/);
const text = response.resultAsText();Define global view
Global view defination
Create a typescript defination script. Ex: api.ts
import mushroom, {
defineAsyncView,
IMushroom,
MushroomRequestSetting
} from "mushroomjs";
interface SampleViewParameters {
param1: string,
param2?: number;
}
interface SampleViewItemResult {
v1: IdType,
v2: boolean,
v3?: number
}
interface SampleView {
sampleAsync(viewParams: SampleViewParameters, settings?: MushroomRequestSetting): Promise<MushroomResponse<SampleViewItemResult[]>>
}
defineAsyncView("sample", { clientCache: true });
mushroom.$using("your absolute root API URL");
interface MushroomApi extends IMushroom {
$view: SampleView
}
export default mushroom as MushroomApi;Global view usage
import mushroom from "./api"
async function example() {
let result = await mushroom.$views.sample({
param1: "abc",
param2: 1
});
}Event
Register event handler
import mushroom, { MushroomRestfulRequest, MushroomRequest, MushroomError } from "mushroomjs";
mushroom._on("eventName", fnEventHandler);Remove event handler
mushroom._unbindEvent("eventName"); // remove all event handlers of 'eventName' event
mushroom._unbindEvent("eventName", fnEventHandler); // remove specific event handler of 'eventName' eventReflection
mushroom._hasEvent("eventName"); // return true if eventName has handler(s)Built-in events
On request begining
mushroom._on("beginRequest", (args: BeginMushroomRequestHandlerArguments) => {});Affter request ended
mushroom._on("endRequest", (args: EndMushroomRequestHandlerArguments) => {});Before sending request
mushroom._on("beforeSend", (request: MushroomRestfulRequest, rawRequest: MushroomRequest) => {});Switch to online state
mushroom._on("online", () => {});Fall to offline state
mushroom._on("offline", () => {});API URL
mushroom.$using(rootApiUrl); // set root API URL
let url = mushroom.$using(); // get current root API URL[Advanced] Settings for each request
preferRole
override cache settings
To override global cache age, see Request level at Cache age
override global request timeout
To override global request timeout
mushroom.foo.listAsync(arg, {
timeout: 3000, // timeout after 3000 miliseconds or 3 seconds
});inject events for each request
mushroom.foo.listAsync(arg, {
beforeSend: (request, rawRequest) => {}
});abort request
To abort a request, use AbortController in setting. Ex:
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 500);
try {
const res = await mushroom.foo.listAsync({}, {
abortController: abortController
});
console.log(res);
}
catch (e) {
console.error(e);
}Custom methods:
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 500);
try {
const res = await mushroom.foo.barAsync({
settings: {
abortController: abortController
}
});
console.log(res);
}
catch (e) {
console.error(e);
}Caching
By default, mushroom js driver supports client cache for 2 methods listAsync and findByIdAsync if actions.findMany.clientCache == true, actions.findById.clientCache == true
Please see Defination at Define resource.
Mushroom js driver only supports client cache for request which is GET method.
Invalid cache
Clear resource cache
mushroom.foo.invalidCache();Clear cache by url
mushroom.$cache.invalid(url) // url: string - a RESTful url will be invalid cacheClear cache by pattern
mushroom.$cache.invalid(pattern) // pattern: RegEx - a regular expression of RESTful urls will be invalid cacheClear all cache
mushroom.$cache.invalid()Cache age
Global level
mushroom.$setting.set("request.cache.age", ms); // set global cache age value (in milisecond)Request level
let result1 = await mushroom.foo.listAsync({ }, {
cacheAge: 5000 // cache in 5 seconds
});
let result2 = await mushroom.foo.findByIdAsync({
id: "your id"
}, {
cacheAge: 10000 // cache in 10 seconds
});
let result = await mushroom.foo.customMethodAsync({ }, {
cacheAge: 15000 // cache in 15 seconds
});Settings
Setting global cache age
mushroom.$setting.set("request.cache.age", 300000); // default: 5 minutesSetting global request timeout
mushroom.$setting.set("request.timeout", timeout_in_ms); // default: undefined (mean: depending on each browser/system)Setting common HTTP methods
mushroom.$setting.set("request.common_methods", ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]);Setting flags
Log generic information
mushroom.$setting.set("diagnostic.log_info", true); // default: falseWarning on slow connection
mushroom.$setting.set("diagnostic.warning_slow_connection", true); // default: falseSlow connection info will be outputed at console.
mushroom.$setting.set("diagnostic.slow_connection_milliseconds", 2000); // default: 2 secondsSlow connection if an API was requested that took more than diagnostic.slow_connection_milliseconds
Log request
mushroom.$setting.set("diagnostic.log_request", true); // default: falseLog response
mushroom.$setting.set("diagnostic.log_response", true); // default: falseLog cache hit
mushroom.$setting.set("diagnostic.log_cache_hit", true); // default: falseFor NodeJS
Required addition dependency: node-fetch
Custom extension
Custom extension defination
mushroom.$ext = mushroom.$ext || {};
mushroom.$ext.method1Async = ... // see Defination of Custom method aboveCustom extension usage
await mushroom.$ext.method1Async(1, 2, 3);Builders
Filter builder
Usage
import { Filter } from 'mushroomjs';
let filter : IBuilder;
// create filter here
mushroom.foo.listAsync({
filters: filter.build()
})eq
filter = Filter.eq("x", 10); // x=10ne
filter = Filter.ne("x", 10); // x!=10lt
filter = Filter.lt("x", 10); // x<10lte
filter = Filter.lte("x", 10); // x<=10gt
filter = Filter.gt("x", 10); // x>10gte
filter = Filter.gte("x", 10); // x>=10min
Alias of gte
filter = Filter.min("x", 10); // x>=10max
Alias of lte
filter = Filter.max("x", 10); // x<=10in
filter = Filter.in("x", [1, 2, 3]); // x:in:1,2,3nin
filter = Filter.nin("x", [1, 2, 3]); // x:nin:1,2,3all
filter = Filter.all("x", [1, 2, 3]); // x:all:1,2,3like
filter = Filter.like("x", "%abc%"); // x:like:%abc% (% will be url-encoded to %25)regex
Without options
filter = Filter.regex("x", /ab[cd]/); // x:regex:ab[cd]
filter = Filter.regex("x", "ab[cd]"); // x:regex:ab[cd]With options
filter = Filter.regex("x", /ab[cd]/i); // x:regex_i:ab[cd]
filter = Filter.regex("x", "ab[cd]", "i"); // x:regex_i:ab[cd]
filter = Filter.regex("x", /ab[cd]/m); // x:regex_m:ab[cd]
filter = Filter.regex("x", "ab[cd]", "m"); // x:regex_m:ab[cd]
filter = Filter.regex("x", /ab[cd]/im); // x:regex_im:ab[cd]
filter = Filter.regex("x", "ab[cd]", "im"); // x:regex_im:ab[cd]elementMatch
filter = Filter.elementMatch("x", [Filter.eq("y", 10), Filter.gt("z", 3)]); // x:elemMatch:y=10,z>3nelementMatch
filter = Filter.nelementMatch("x", [Filter.eq("y", 10), Filter.gt("z", 3)]); // x:nelemMatch:y=10,z>3size
filter = Filter.size("x", 4); // x:size:4isNull
filter = Filter.isNull("x"); // x:is_null:true
filter = Filter.isNull("x", false); // x:is_null:falseand
filter = Filter.and([Filter.eq("y", 10), Filter.gt("z", 3)]); // y=10&z>3
filter = Filter.and(Filter.eq("y", 10), Filter.gt("z", 3)); // y=10&z>3or
filter = Filter.or([Filter.eq("y", 10), Filter.gt("z", 3)]); // y=10|z>3
filter = Filter.or(Filter.eq("y", 10), Filter.gt("z", 3)); // y=10|z>3empty
filter = Filter.empty(); // empty stringfilter chain
filter = Filter.eq("a", 1).lt("b", 2); // a=1&b<2
filter = Filter.eq("a", 1).lt("b", 2).or(Filter.eq("c", 3).gte("d", 4)); // :and:a=1,b=2|:and:c=3,d=4
filter = Filter.eq("a", 1).lt("b", 2).and(Filter.or(Filter.eq("c", 3), Filter.gte("d", 4))); // a=1&b=2&c=3|d=4
filter = Filter.empty().eq("a", 1); // a=1toArray
filters = Filter.eq("a", 1).lt("b", 2).toArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.eq("a", 1).or("b", 2).toArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.elementMatch("x", [Filter.eq("a", 1), Filter.lt("b", 2)]).toArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.eq("a", 1).toArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]asArray
filters = Filter.eq("a", 1).lt("b", 2).asArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.eq("a", 1).or("b", 2).asArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.elementMatch("x", [Filter.eq("a", 1), Filter.lt("b", 2)]).asArray() // [Filter.eq("a", 1), Filter.lt("b", 2)]
filters = Filter.eq("a", 1).asArray() // Errorvariables
filters = Filter.eq("d", Filter.variables.now()) // d=$$now
filters = Filter.eq("d", Filter.variables.today()) // d=$$today
filters = Filter.eq("d", Filter.variables.userId()) // d=$$user_id
filters = Filter.eq("ip", Filter.variables.userIp()) // ip=$$user_ip
filters = Filter.eq("d", Filter.variables.nowAdd(5, 'hour')) // d=$$now+5hours
filters = Filter.eq("d", Filter.variables.todayAdd(2, 'week')) // d=$$today+2weeks
filters = Filter.in("company_id", Filter.variables.fromName<string>('company_id')) // company_id=$$company_id
filters = Filter.in("number", Filter.variables.fromName<number[]>('my_numbers')) // number:in:$$my_numbersSort builder
Usage
import { Sort } from 'mushroomjs';
let sort : IBuilder;
// create sort here
mushroom.foo.listAsync({
sort: sort.build()
})ascending
sort = Sort.ascending("x"); // xdescending
sort = Sort.descending("x"); // -xthenByAscending
sort = Sort.ascending("x").thenByAscending("y"); // x,ythenByDescending
sort = Sort.ascending("x").thenByDescending("y"); // x,-yProject builder
Usage
import { Project } from 'mushroomjs';
let project : IBuilder;
// create project here
mushroom.foo.listAsync({
fields: project.build()
})include
project = Project.include("x"); // x
project = Project.include("x", "y"); // x,y
project = Project.include("x").include("y"); // x,yRelease notes
1.2.0
New features:
- Add new setting to specify the common HTTP methods (not need to use header X-HTTP-Method-Override):
request.common_methods - Add new rest object field in
MushroomRestfulRequestto customizefetch - Allow to customize request headers and params of common actions (such as findById, list, create...)
- Support raw request data
- Support variables in filter builder
1.1.1
Fix bug:
- Get wrong result when using nested
andfilter in FilterBuilder
1.1.0
New features:
- Support Typescript
- Support NodeJS
- Support Abort request
- Support softDelete
- Support
preferRolefor request - Support
channelforvn_text - Add builders (Filter, Sort, Project)
- Add new settings for log actions, such as
diagnostic.log_info,diagnostic.log_cache_hit - Add new methods
countAsync,getAllAsyncfor resource - Add new methods
resultAsBlob,resultAsTextfor reponse.
Fix bugs:
- iOS: missing escape url for filter
- Missing headers which were passed in each api call time
- Wrong in some cases when call api
deleteAsyncandbatchDeleteAsync
