Skip to content

Commit

Permalink
refactor: parameter parsing & enhanced options api
Browse files Browse the repository at this point in the history
  • Loading branch information
tada5hi committed Oct 17, 2022
1 parent cb4cafb commit 5361e12
Show file tree
Hide file tree
Showing 43 changed files with 864 additions and 538 deletions.
13 changes: 12 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
},
"homepage": "https://github.com/Tada5hi/rapiq#readme",
"dependencies": {
"minimatch": "^5.1.0"
"minimatch": "^5.1.0",
"smob": "^0.0.3"
},
"devDependencies": {
"@commitlint/cli": "^17.1.2",
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export enum URLParameter {

// -----------------------------------------------------------

export const DEFAULT_ALIAS_ID = '__DEFAULT__';
export const DEFAULT_ID = '__DEFAULT__';
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './build';
export * from './parameter';
export * from './parse';
export * from './constants';
export * from './type';
5 changes: 2 additions & 3 deletions src/parameter/fields/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
* view the LICENSE file that was distributed with this source code.
*/

import { flattenNestedProperties } from '../utils';
import { FieldsBuildInput } from './type';
import { mergeDeep } from '../../utils';
import { flattenNestedObject, mergeDeep } from '../../utils';

export function buildQueryFieldsForMany<T>(
inputs: FieldsBuildInput<T>[],
Expand Down Expand Up @@ -39,6 +38,6 @@ export function buildQueryFields<T>(
case Array.isArray(data):
return data;
default:
return flattenNestedProperties(data as Record<string, any>);
return flattenNestedObject(data as Record<string, any>);
}
}
99 changes: 43 additions & 56 deletions src/parameter/fields/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
* view the LICENSE file that was distributed with this source code.
*/

import { merge } from 'smob';
import {
buildFieldWithAlias, getNameByAliasMapping, hasOwnProperty, isAllowedByRelations,
applyMapping, buildFieldWithPath, groupArrayByKeyPath, hasOwnProperty, isFieldPathAllowedByRelations,
} from '../../utils';
import { flattenParseOptionsAllowed } from '../utils';
import {
FieldsInputTransformed, FieldsParseOptions, FieldsParseOutput,
} from './type';
import {
buildFieldDomainRecords,
mergeFieldsDomainRecords,
parseFieldsInput, removeFieldInputOperator,
transformFieldsInput,
} from './utils';
import { DEFAULT_ALIAS_ID } from '../../constants';
import { DEFAULT_ID } from '../../constants';

// --------------------------------------------------

Expand Down Expand Up @@ -52,70 +52,58 @@ export function parseQueryFields(
) : FieldsParseOutput {
options ??= {};

const defaultDomainFields = buildFieldDomainRecords(options.default);
const domainFields = mergeFieldsDomainRecords(
buildFieldDomainRecords(options.allowed),
{ ...defaultDomainFields },
const defaultDomainFields = groupArrayByKeyPath(
flattenParseOptionsAllowed(options.default),
);

const allowedDomainFields = groupArrayByKeyPath(
flattenParseOptionsAllowed(options.allowed),
);

const domainFields = merge(
{},
defaultDomainFields,
allowedDomainFields,
);

let domainKeys : string[] = Object.keys(domainFields);

// If it is an empty array nothing is allowed
if (domainKeys.length === 0) {
return [];
}

if (options.defaultAlias) {
if (hasOwnProperty(defaultDomainFields, DEFAULT_ALIAS_ID)) {
replaceRecordKey(defaultDomainFields, DEFAULT_ALIAS_ID, options.defaultAlias);
}

if (hasOwnProperty(domainFields, DEFAULT_ALIAS_ID)) {
replaceRecordKey(domainFields, DEFAULT_ALIAS_ID, options.defaultAlias);
}
}

domainKeys = Object.keys(domainFields);

let defaultAlias : string;

if (
domainKeys.length === 1 &&
!options.defaultAlias
) {
// eslint-disable-next-line prefer-destructuring
defaultAlias = domainKeys[0];
} else {
defaultAlias = options.defaultAlias || DEFAULT_ALIAS_ID;
}

options.defaultAlias = defaultAlias;

const prototype: string = Object.prototype.toString.call(data);
if (
prototype !== '[object Object]' &&
prototype !== '[object Array]' &&
prototype !== '[object String]'
) {
data = { [defaultAlias]: [] };
data = { [DEFAULT_ID]: [] };
}

if (prototype === '[object String]') {
data = { [defaultAlias]: data };
data = { [DEFAULT_ID]: data };
}

if (prototype === '[object Array]') {
data = { [defaultAlias]: data };
data = { [DEFAULT_ID]: data };
}

options.aliasMapping ??= {};
const reverseAliasMapping = buildReverseRecord(options.aliasMapping);
options.mapping ??= {};
const reverseMapping = buildReverseRecord(options.mapping);

const output : FieldsParseOutput = [];

for (let i = 0; i < domainKeys.length; i++) {
const domainKey = domainKeys[i];

if (!isAllowedByRelations({ alias: domainKey }, options.relations, options.defaultAlias)) {
if (
!isFieldPathAllowedByRelations({ path: domainKey }, options.relations) &&
domainKey !== DEFAULT_ID
) {
continue;
}

Expand All @@ -126,10 +114,10 @@ export function parseQueryFields(
) {
fields = parseFieldsInput(data[domainKey]);
} else if (
hasOwnProperty(reverseAliasMapping, domainKey)
hasOwnProperty(reverseMapping, domainKey)
) {
if (hasOwnProperty(data, reverseAliasMapping[domainKey])) {
fields = parseFieldsInput(data[reverseAliasMapping[domainKey]]);
if (hasOwnProperty(data, reverseMapping[domainKey])) {
fields = parseFieldsInput(data[reverseMapping[domainKey]]);
}
}

Expand All @@ -141,9 +129,9 @@ export function parseQueryFields(

if (fields.length > 0) {
for (let j = 0; j < fields.length; j++) {
fields[j] = getNameByAliasMapping(
buildFieldWithAlias({ name: fields[j], alias: domainKey }),
options.aliasMapping,
fields[j] = applyMapping(
buildFieldWithPath({ name: fields[j], path: domainKey }),
options.mapping,
true,
);
}
Expand All @@ -159,19 +147,18 @@ export function parseQueryFields(
}

if (
transformed.default.length === 0
transformed.default.length === 0 &&
hasOwnProperty(defaultDomainFields, domainKey)
) {
if (hasOwnProperty(defaultDomainFields, domainKey)) {
transformed.default = defaultDomainFields[domainKey];
}
transformed.default = defaultDomainFields[domainKey];
}

if (
transformed.included.length === 0 &&
transformed.default.length === 0 &&
hasOwnProperty(domainFields, domainKey)
) {
transformed.default = domainFields[domainKey];
}
if (
transformed.included.length === 0 &&
transformed.default.length === 0 &&
hasOwnProperty(allowedDomainFields, domainKey)
) {
transformed.default = allowedDomainFields[domainKey];
}

transformed.default = Array.from(new Set([
Expand All @@ -190,7 +177,7 @@ export function parseQueryFields(
for (let j = 0; j < transformed.default.length; j++) {
output.push({
key: transformed.default[j],
...(domainKey !== DEFAULT_ALIAS_ID ? { alias: domainKey } : {}),
...(domainKey !== DEFAULT_ID ? { path: domainKey } : {}),
});
}
}
Expand Down
55 changes: 34 additions & 21 deletions src/parameter/fields/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,57 @@
* view the LICENSE file that was distributed with this source code.
*/

import { Parameter } from '../../constants';
import {
Flatten,
KeyWithOptionalPrefix,
OnlyObject,
ParseOptionsBase,
ParseOutputElementBase,
ToOneAndMany,
Flatten, KeyWithOptionalPrefix, NestedKeys, OnlyObject, SimpleKeys,
} from '../../type';
import { RelationsParseOutput } from '../relations';
import {
ParseOptionsAllowed,
} from '../type';
import { FieldOperator } from './constants';

// -----------------------------------------------------------
// Build
// -----------------------------------------------------------

type FieldWithOperator<T extends Record<string, any>> =
KeyWithOptionalPrefix<keyof T, FieldOperator> |
KeyWithOptionalPrefix<keyof T, FieldOperator>[];
type FieldWithOperator<T extends string> = KeyWithOptionalPrefix<T, FieldOperator>;

export type FieldsBuildInput<T extends Record<string, any>> =
{
[K in keyof T]?: T[K] extends OnlyObject<T[K]> ?
(FieldsBuildInput<Flatten<T[K]>> | FieldWithOperator<Flatten<T[K]>>) : never
} |
{
[key: string]: ToOneAndMany<KeyWithOptionalPrefix<keyof T, FieldOperator>[]>,
} |
FieldWithOperator<T>;
{
[K in keyof T]?: Flatten<T[K]> extends OnlyObject<T[K]> ?
FieldsBuildInput<Flatten<T[K]>> :
never
}
|
[
FieldWithOperator<SimpleKeys<T>>[],
{
[K in keyof T]?: Flatten<T[K]> extends OnlyObject<T[K]> ?
FieldsBuildInput<Flatten<T[K]>> :
never
},
]
|
FieldWithOperator<NestedKeys<T>>[];

// -----------------------------------------------------------
// Parse
// -----------------------------------------------------------

export type FieldsParseOptions = ParseOptionsBase<Parameter.FIELDS, Record<string, string[]> | string[]> & {
default?: Record<string, string[]> | string[]
export type FieldsParseOptions<
T extends Record<string, any> = Record<string, any>,
> = {
mapping?: Record<string, string>,
allowed?: ParseOptionsAllowed<T>,
default?: ParseOptionsAllowed<T>,
relations?: RelationsParseOutput
};

export type FieldsParseOutputElement = ParseOutputElementBase<Parameter.FIELDS, FieldOperator>;
export type FieldsParseOutputElement = {
key: string,
path?: string,
value?: string
};
export type FieldsParseOutput = FieldsParseOutputElement[];

export type FieldsInputTransformed = {
Expand Down
12 changes: 6 additions & 6 deletions src/parameter/fields/utils/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { hasOwnProperty } from '../../../utils';
import { DEFAULT_ALIAS_ID } from '../../../constants';
import { DEFAULT_ID } from '../../../constants';

export function buildFieldDomainRecords(
data?: Record<string, string[]> | string[],
Expand All @@ -18,7 +18,7 @@ export function buildFieldDomainRecords(
let domainFields: Record<string, string[]> = {};

if (Array.isArray(data)) {
domainFields[DEFAULT_ALIAS_ID] = data;
domainFields[DEFAULT_ID] = data;
} else {
domainFields = data;
}
Expand Down Expand Up @@ -54,11 +54,11 @@ export function mergeFieldsDomainRecords(

if (
keys.length >= 2 &&
hasOwnProperty(target, DEFAULT_ALIAS_ID) &&
Array.isArray(target[DEFAULT_ALIAS_ID]) &&
target[DEFAULT_ALIAS_ID].length === 0
hasOwnProperty(target, DEFAULT_ID) &&
Array.isArray(target[DEFAULT_ID]) &&
target[DEFAULT_ID].length === 0
) {
delete target[DEFAULT_ALIAS_ID];
delete target[DEFAULT_ID];
}

return target;
Expand Down
Loading

0 comments on commit 5361e12

Please sign in to comment.