Skip to content

Commit

Permalink
feat: Add hook-less methods and service option types to adapter-commo…
Browse files Browse the repository at this point in the history
…ns (#1433)
  • Loading branch information
daffl authored Jul 5, 2019
1 parent 350e1c3 commit 857f54a
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 107 deletions.
1 change: 1 addition & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ plugins:
count_threshold: 3
exclude_patterns:
- "**/test/*"
- "**/adapter-commons/src/sort.ts"
- "**/adapter-tests/lib/*"
- "**/dist/*"
- "**/*.dist.js"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dist/
.mocha-puppeteer

# TypeScript compiled files
packages/adapter-commons/lib
packages/authentication/lib
packages/authentication-local/lib
packages/authentication-client/lib
Expand Down
3 changes: 2 additions & 1 deletion .nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"**/test/*",
"**/dist/*",
"**/*.dist.js",
"**/templates/*"
"**/templates/*",
"**/adapter-commons/src/sort.ts"
],
"print": "detail",
"reporter": [
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-commons/.npmignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
test/
tsconfig.json
21 changes: 15 additions & 6 deletions packages/adapter-commons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,30 @@
},
"main": "lib/index.js",
"scripts": {
"test": "mocha --opts ../../mocha.opts"
"prepublish": "npm run compile",
"compile": "shx rm -rf lib/ && tsc",
"test": "mocha --opts ../../mocha.ts.opts --recursive test/**.test.ts test/**/*.test.ts"
},
"directories": {
"lib": "lib"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"mocha": "^6.1.4",
"mongodb": "^3.2.6"
},
"dependencies": {
"@feathersjs/feathers": "^4.0.0-pre.3",
"@feathersjs/commons": "^4.0.0-pre.3",
"@feathersjs/errors": "^4.0.0-pre.3"
},
"gitHead": "19eb75737659e3e4b57765c1653d23c86fd2e1c3"
"devDependencies": {
"@types/mongodb": "^3.1.28",
"mocha": "^6.1.4",
"mongodb": "^3.2.6",
"@types/mocha": "^5.2.6",
"@types/node": "^12.0.2",
"mocha": "^6.1.4",
"shx": "^0.3.2",
"ts-node": "^8.2.0",
"typescript": "^3.4.5"
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const { _ } = require('@feathersjs/commons');
const { BadRequest } = require('@feathersjs/errors');
import { _ } from '@feathersjs/commons';
import { BadRequest } from '@feathersjs/errors';

function parse (number) {
function parse (number: any) {
if (typeof number !== 'undefined') {
return Math.abs(parseInt(number, 10));
}

return undefined;
}

// Returns the pagination limit and will take into account the
// default and max pagination settings
function getLimit (limit, paginate) {
function getLimit (limit: any, paginate: any) {
if (paginate && paginate.default) {
const lower = typeof limit === 'number' ? limit : paginate.default;
const upper = typeof paginate.max === 'number' ? paginate.max : Number.MAX_VALUE;
Expand All @@ -21,7 +23,7 @@ function getLimit (limit, paginate) {
}

// Makes sure that $sort order is always converted to an actual number
function convertSort (sort) {
function convertSort (sort: any) {
if (typeof sort !== 'object' || Array.isArray(sort)) {
return sort;
}
Expand All @@ -31,12 +33,12 @@ function convertSort (sort) {
? sort[key] : parseInt(sort[key], 10);

return result;
}, {});
}, {} as { [key: string]: number });
}

function cleanQuery (query, operators, filters) {
function cleanQuery (query: any, operators: any, filters: any) {
if (_.isObject(query) && query.constructor === {}.constructor) {
const result = {};
const result: { [key: string]: any } = {};

_.each(query, (value, key) => {
if (key[0] === '$') {
Expand All @@ -53,6 +55,7 @@ function cleanQuery (query, operators, filters) {
});

Object.getOwnPropertySymbols(query).forEach(symbol => {
// @ts-ignore
result[symbol] = query[symbol];
});

Expand All @@ -62,7 +65,7 @@ function cleanQuery (query, operators, filters) {
return query;
}

function assignFilters (object, query, filters, options) {
function assignFilters (object: any, query: any, filters: any, options: any) {
if (Array.isArray(filters)) {
_.each(filters, (key) => {
if (query[key] !== undefined) {
Expand All @@ -82,31 +85,33 @@ function assignFilters (object, query, filters, options) {
return object;
}

const FILTERS = {
$sort: (value) => convertSort(value),
$limit: (value, options) => getLimit(parse(value), options.paginate),
$skip: (value) => parse(value),
$select: (value) => value
export const FILTERS = {
$sort: (value: any) => convertSort(value),
$limit: (value: any, options: any) => getLimit(parse(value), options.paginate),
$skip: (value: any) => parse(value),
$select: (value: any) => value
};

const OPERATORS = ['$in', '$nin', '$lt', '$lte', '$gt', '$gte', '$ne', '$or'];
export const OPERATORS = ['$in', '$nin', '$lt', '$lte', '$gt', '$gte', '$ne', '$or'];

// Converts Feathers special query parameters and pagination settings
// and returns them separately a `filters` and the rest of the query
// as `query`
module.exports = function filterQuery (query, options = {}) {
export default function filterQuery (query: any, options: any = {}) {
const {
filters: additionalFilters = {},
operators: additionalOperators = []
} = options;
const result = {};
const result: { [key: string]: any } = {};

result.filters = assignFilters({}, query, FILTERS, options);
result.filters = assignFilters(result.filters, query, additionalFilters, options);

result.query = cleanQuery(query, OPERATORS.concat(additionalOperators), result.filters);

return result;
};
}

Object.assign(module.exports, { OPERATORS, FILTERS });
if (typeof module !== 'undefined') {
module.exports = Object.assign(filterQuery, module.exports);
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
const { _ } = require('@feathersjs/commons');
import { _ } from '@feathersjs/commons';

const AdapterService = require('./service');
const filterQuery = require('./filter-query');
const sort = require('./sort');
export { AdapterService, InternalServiceMethods, ServiceOptions } from './service';
export { default as filterQuery, FILTERS, OPERATORS } from './filter-query';
export * from './sort';

// Return a function that filters a result object or array
// and picks only the fields passed as `params.query.$select`
// and additional `otherFields`
const select = function select (params, ...otherFields) {
export function select (params: any, ...otherFields: any[]) {
const fields = params && params.query && params.query.$select;

if (Array.isArray(fields) && otherFields.length) {
fields.push(...otherFields);
}

const convert = result => {
const convert = (result: any) => {
if (!Array.isArray(fields)) {
return result;
}

return _.pick(result, ...fields);
};

return result => {
return (result: any) => {
if (Array.isArray(result)) {
return result.map(convert);
}

return convert(result);
};
};

module.exports = Object.assign({
select,
filterQuery,
AdapterService
}, sort);
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,50 @@
const { NotImplemented, BadRequest, MethodNotAllowed } = require('@feathersjs/errors');
const filterQuery = require('./filter-query');
import { NotImplemented, BadRequest, MethodNotAllowed } from '@feathersjs/errors';
import { ServiceMethods, Params, Paginated, Id, NullableId } from '@feathersjs/feathers';
import filterQuery from './filter-query';

const callMethod = (self, name, ...args) => {
const callMethod = (self: any, name: any, ...args: any[]) => {
if (typeof self[name] !== 'function') {
return Promise.reject(new NotImplemented(`Method ${name} not available`));
}

return self[name](...args);
};

const alwaysMulti = {
const alwaysMulti: { [key: string]: boolean } = {
find: true,
get: false,
update: false
};

module.exports = class AdapterService {
constructor (options) {
export interface ServiceOptions {
events: string[];
multi: boolean|string[];
id: string;
paginate: any;
whitelist: string[];
filters: string[];
}

export interface InternalServiceMethods<T = any> {
_find (params?: Params): Promise<T | T[] | Paginated<T>>;
_get (id: Id, params?: Params): Promise<T>;
_create (data: Partial<T> | Array<Partial<T>>, params?: Params): Promise<T | T[]>;
_update (id: NullableId, data: T, params?: Params): Promise<T>;
_patch (id: NullableId, data: Partial<T>, params?: Params): Promise<T>;
_remove (id: NullableId, params?: Params): Promise<T>;
}

export class AdapterService<T = any> implements ServiceMethods<T> {
options: ServiceOptions;

constructor (options: Partial<ServiceOptions>) {
this.options = Object.assign({
id: 'id',
events: [],
paginate: {},
multi: false
multi: false,
filters: [],
whitelist: []
}, options);
}

Expand All @@ -32,7 +56,7 @@ module.exports = class AdapterService {
return this.options.events;
}

filterQuery (params = {}, opts = {}) {
filterQuery (params: Params = {}, opts: any = {}) {
const paginate = typeof params.paginate !== 'undefined'
? params.paginate : this.options.paginate;
const { query = {} } = params;
Expand All @@ -46,7 +70,7 @@ module.exports = class AdapterService {
return Object.assign(result, { paginate });
}

allowsMulti (method) {
allowsMulti (method: string) {
const always = alwaysMulti[method];

if (typeof always !== 'undefined') {
Expand All @@ -62,23 +86,23 @@ module.exports = class AdapterService {
}
}

find (params) {
find (params?: Params): Promise<T | T[] | Paginated<T>> {
return callMethod(this, '_find', params);
}

get (id, params) {
get (id: Id, params?: Params): Promise<T> {
return callMethod(this, '_get', id, params);
}

create (data, params) {
create (data: Partial<T> | Array<Partial<T>>, params?: Params): Promise<T | T[]> {
if (Array.isArray(data) && !this.allowsMulti('create')) {
return Promise.reject(new MethodNotAllowed(`Can not create multiple entries`));
}

return callMethod(this, '_create', data, params);
}

update (id, data, params) {
update (id: NullableId, data: T, params?: Params): Promise<T> {
if (id === null || Array.isArray(data)) {
return Promise.reject(new BadRequest(
`You can not replace multiple instances. Did you mean 'patch'?`
Expand All @@ -88,19 +112,19 @@ module.exports = class AdapterService {
return callMethod(this, '_update', id, data, params);
}

patch (id, data, params) {
patch (id: NullableId, data: Partial<T>, params?: Params): Promise<T> {
if (id === null && !this.allowsMulti('patch')) {
return Promise.reject(new MethodNotAllowed(`Can not patch multiple entries`));
}

return callMethod(this, '_patch', id, data, params);
}

remove (id, params) {
remove (id: NullableId, params?: Params): Promise<T> {
if (id === null && !this.allowsMulti('remove')) {
return Promise.reject(new MethodNotAllowed(`Can not remove multiple entries`));
}

return callMethod(this, '_remove', id, params);
}
};
}
Loading

0 comments on commit 857f54a

Please sign in to comment.