npm.io
1.2.0 • Published yesterday

mushroomjs

Licence
ISC
Version
1.2.0
Deps
0
Size
158 kB
Vulns
0
Weekly
0

Install

npm install mushroomjs

or

yarn add mushroomjs

Import

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' event

Reflection

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

See Call as prefer role

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 cache

Clear cache by pattern

mushroom.$cache.invalid(pattern) // pattern: RegEx - a regular expression of RESTful urls will be invalid cache

Clear 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 minutes

Setting 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: false
Warning on slow connection
mushroom.$setting.set("diagnostic.warning_slow_connection", true); // default: false

Slow connection info will be outputed at console.

mushroom.$setting.set("diagnostic.slow_connection_milliseconds", 2000); // default: 2 seconds

Slow connection if an API was requested that took more than diagnostic.slow_connection_milliseconds

Log request
mushroom.$setting.set("diagnostic.log_request", true); // default: false
Log response
mushroom.$setting.set("diagnostic.log_response", true); // default: false
Log cache hit
mushroom.$setting.set("diagnostic.log_cache_hit", true); // default: false

For NodeJS

Required addition dependency: node-fetch

Custom extension

Custom extension defination

mushroom.$ext = mushroom.$ext || {};
mushroom.$ext.method1Async = ... // see Defination of Custom method above

Custom 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=10
ne
filter = Filter.ne("x", 10); // x!=10
lt
filter = Filter.lt("x", 10); // x<10
lte
filter = Filter.lte("x", 10); // x<=10
gt
filter = Filter.gt("x", 10); // x>10
gte
filter = Filter.gte("x", 10); // x>=10
min

Alias of gte

filter = Filter.min("x", 10); // x>=10
max

Alias of lte

filter = Filter.max("x", 10); // x<=10
in
filter = Filter.in("x", [1, 2, 3]); // x:in:1,2,3
nin
filter = Filter.nin("x", [1, 2, 3]); // x:nin:1,2,3
all
filter = Filter.all("x", [1, 2, 3]); // x:all:1,2,3
like
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>3
nelementMatch
filter = Filter.nelementMatch("x", [Filter.eq("y", 10), Filter.gt("z", 3)]); // x:nelemMatch:y=10,z>3
size
filter = Filter.size("x", 4); // x:size:4
isNull
filter = Filter.isNull("x"); // x:is_null:true
filter = Filter.isNull("x", false); // x:is_null:false
and
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>3
or
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>3
empty
filter = Filter.empty(); // empty string
filter 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=1
toArray
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() // Error
variables
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_numbers

Sort builder

Usage

import { Sort } from 'mushroomjs';

let sort : IBuilder;

// create sort here

mushroom.foo.listAsync({
    sort: sort.build()
})
ascending
sort = Sort.ascending("x"); // x
descending
sort = Sort.descending("x"); // -x
thenByAscending
sort = Sort.ascending("x").thenByAscending("y"); // x,y
thenByDescending
sort = Sort.ascending("x").thenByDescending("y"); // x,-y

Project 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,y

Release 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 MushroomRestfulRequest to customize fetch
  • 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 and filter in FilterBuilder

1.1.0

New features:

  • Support Typescript
  • Support NodeJS
  • Support Abort request
  • Support softDelete
  • Support preferRole for request
  • Support channel for vn_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, getAllAsync for resource
  • Add new methods resultAsBlob, resultAsText for 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 deleteAsync and batchDeleteAsync

Keywords