Skip to content

Commit 0428c85

Browse files
authored
Merge pull request #67 from micro-analytics/feat/add-adapter-details
feat: Add adapter-utils
2 parents 99d23e7 + 07c5029 commit 0428c85

File tree

5 files changed

+184
-1
lines changed

5 files changed

+184
-1
lines changed

adapter-utils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const supportsAsyncAwait =
2+
parseInt(process.version.slice(1).split('.').join('')) > 760;
3+
4+
const path = supportsAsyncAwait
5+
? './src/adapter-utils'
6+
: './dist/adapter-utils';
7+
8+
module.exports = require(path);

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@
3434
]
3535
},
3636
"lint-staged": {
37-
"*.js": ["prettier --single-quote --trailing-comma es5 --write", "git add"]
37+
"*.js": [
38+
"prettier --single-quote --trailing-comma es5 --write",
39+
"git add"
40+
]
3841
},
3942
"author": "Max Stoiber <[email protected]> (http://mxstbr.com/)",
4043
"license": "MIT",
4144
"repository": "https://github.com/mxstbr/micro-analytics",
4245
"dependencies": {
4346
"args": "^2.3.0",
47+
"escape-regex": "^1.0.7",
4448
"flat-file-db": "^1.0.0",
4549
"micro": "6.1.0",
4650
"micro-analytics-adapter-flat-file-db": "^1.3.0",

src/adapter-utils.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const escapeRegexp = require('escape-regex');
2+
3+
function createKeyRegex(str) {
4+
str = str.split('*').map(s => escapeRegexp(s)).join('*');
5+
return new RegExp('^' + str.replace('*', '.*'));
6+
}
7+
8+
function createPathFilter(options) {
9+
return key =>
10+
options.ignoreWildcard
11+
? key.startsWith(options.pathname)
12+
: key.match(createKeyRegex(options.pathname));
13+
}
14+
15+
function filterPaths(paths, options) {
16+
return (paths || []).filter(createPathFilter(options));
17+
}
18+
19+
function createViewFilter(options) {
20+
return view => {
21+
if (options && options.before && view.time > options.before) return false;
22+
if (options && options.after && view.time < options.after) return false;
23+
return true;
24+
};
25+
}
26+
27+
function filterViews(views, options) {
28+
return (views || []).filter(createViewFilter(options));
29+
}
30+
31+
module.exports = {
32+
createKeyRegex,
33+
createPathFilter,
34+
filterPaths,
35+
createViewFilter,
36+
filterViews,
37+
};

tests/adapter-utils.test.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const {
2+
createKeyRegex,
3+
createPathFilter,
4+
filterPaths,
5+
createViewFilter,
6+
filterViews,
7+
} = require('../src/adapter-utils');
8+
9+
test('createKeyRegex', () => {
10+
expect(createKeyRegex('/path').test('/pathname')).toBe(true);
11+
expect(createKeyRegex('/p*th').test('/pathname')).toBe(true);
12+
expect(createKeyRegex('/path').test('/athname')).toBe(false);
13+
});
14+
15+
test('createPathFilter with ignoreWildcard', () => {
16+
const keys = ['/path', '/other-path', '/pathname', '/p-ath'];
17+
const filterFunc = createPathFilter({
18+
ignoreWildcard: true,
19+
pathname: '/path',
20+
});
21+
22+
expect(keys.filter(filterFunc)).toEqual(['/path', '/pathname']);
23+
});
24+
25+
test('createPathFilter without ignoreWildcard', () => {
26+
const keys = ['/path', '/other-path', '/pathname', '/p-ath'];
27+
const filterFunc = createPathFilter({
28+
ignoreWildcard: false,
29+
pathname: '/p*ath',
30+
});
31+
32+
expect(keys.filter(filterFunc)).toEqual(['/path', '/pathname', '/p-ath']);
33+
});
34+
35+
test('filterPaths should return array when paths is undefined', () => {
36+
expect(filterPaths(undefined)).toEqual([]);
37+
});
38+
39+
test('filterPaths with ignoreWildcard', () => {
40+
const keys = ['/path', '/other-path', '/pathname', '/p-ath'];
41+
const filtered = filterPaths(keys, {
42+
ignoreWildcard: true,
43+
pathname: '/path',
44+
});
45+
46+
expect(filtered).toEqual(['/path', '/pathname']);
47+
});
48+
49+
test('filterPaths without ignoreWildcard', () => {
50+
const keys = ['/path', '/other-path', '/pathname', '/p-ath'];
51+
const filtered = filterPaths(keys, {
52+
ignoreWildcard: false,
53+
pathname: '/p*ath',
54+
});
55+
56+
expect(filtered).toEqual(['/path', '/pathname', '/p-ath']);
57+
});
58+
59+
const one = { time: new Date(2017, 4, 4, 1, 0).getTime() };
60+
const two = { time: new Date(2017, 4, 4, 2, 0).getTime() };
61+
const three = { time: new Date(2017, 4, 4, 3, 0).getTime() };
62+
const four = { time: new Date(2017, 4, 4, 4, 0).getTime() };
63+
const views = [one, two, three, four];
64+
65+
const after = new Date(2017, 4, 4, 2, 30).getTime();
66+
const before = new Date(2017, 4, 4, 3, 30).getTime();
67+
68+
test('createViewFilter should with before filter', () => {
69+
expect(views.filter(createViewFilter({ before }))).toEqual([one, two, three]);
70+
});
71+
72+
test('createViewFilter should with after filter', () => {
73+
expect(views.filter(createViewFilter({ after }))).toEqual([three, four]);
74+
});
75+
76+
test('createViewFilter should with after and before filter', () => {
77+
expect(views.filter(createViewFilter({ after, before }))).toEqual([three]);
78+
});
79+
80+
test('filterViews should return array when views is undefined', () => {
81+
expect(filterViews(undefined)).toEqual([]);
82+
});
83+
84+
test('filterViews should not filter without options', () => {
85+
expect(filterViews(views)).toEqual([one, two, three, four]);
86+
});
87+
88+
test('filterViews should with before filter', () => {
89+
expect(filterViews(views, { before })).toEqual([one, two, three]);
90+
});
91+
92+
test('filterViews should with after filter', () => {
93+
expect(filterViews(views, { after })).toEqual([three, four]);
94+
});
95+
96+
test('filterViews should with after and before filter', () => {
97+
expect(filterViews(views, { after, before })).toEqual([three]);
98+
});

writing-adapters.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ module.exports = {
4444
}
4545
```
4646

47+
## Adapter-utils
48+
49+
Adapters falls mostly into two camps, those who can offload filtering to the database engine and
50+
those who need to do the filtering in JavaScript. If you are writing an adapter for the last group
51+
your in luck. We have a set of utils that are well tested which handles filtering. Read on if you
52+
would like to use or skip to the next section. Keep in mind that you might not need all of these
53+
depending on the database you are using.
54+
55+
### `createKeyRegex`
56+
Creates a regex from a wildcard string, e.g. `/p*th`.
57+
58+
Usage: `createKeyRegex(pathname: string): Regex`
59+
60+
61+
### `createPathFilter`
62+
Creates a filter predicate that can be passed into Array.filter based on options. This might be
63+
useful in `getAll`. See also `filterPaths` below.
64+
65+
Usage: `createPathFilter(options: { ignoreWildcard: boolean, pathname: string }):: (path: string) => boolean`
66+
67+
### `filterPaths`
68+
Filters a list of paths based on given options. This might be useful in `getAll`.
69+
70+
Usage: `filterPaths(paths: Array<string>, options: { ignoreWildcard: boolean, pathname: string }): Array<string>`
71+
72+
### `createViewFilter`
73+
Creates a filter predicate that can be passed into Array.filter based on options. It can be used
74+
with a list of views. This might be useful in `get` and `getAll`. See also `filterViews` below.
75+
76+
Usage: `createViewFilter(options: { before: number, after: number }): (view: View) => boolean`
77+
78+
### `filterViews`
79+
Filters a list of views based on given options. This migth be useful in `get` and `getAll`.
80+
81+
Usage: `filterViews(views: Array<View>, options: { before: number, after: number }): Array<View>`
82+
4783
## Tests
4884

4985
There is pre-written tests for adapters that you can use to make sure you adapter is implemented

0 commit comments

Comments
 (0)