Skip to content

Commit

Permalink
fix: query parameter building + enhanced generation
Browse files Browse the repository at this point in the history
  • Loading branch information
tada5hi committed Oct 18, 2022
1 parent 5361e12 commit 40d56c4
Show file tree
Hide file tree
Showing 16 changed files with 248 additions and 233 deletions.
54 changes: 29 additions & 25 deletions src/build/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@

import { BuildInput, BuildOptions } from './type';
import {
buildQueryFieldsForMany,
buildQueryFiltersForMany,
buildQueryPaginationForMany,
buildQueryRelationsForMany,
buildQuerySortForMany,
buildQueryFields,
buildQueryFilters,
buildQueryRelations,
buildQuerySort,
mergeQueryFields,
mergeQueryFilters,
mergeQueryPagination,
mergeQueryRelations,
mergeQuerySort,
} from '../parameter';
import { Parameter, URLParameter } from '../constants';
import {
Expand All @@ -35,50 +39,50 @@ export function buildQuery<T extends Record<string, any>>(
typeof input[Parameter.FIELDS] !== 'undefined' ||
typeof input[URLParameter.FIELDS] !== 'undefined'
) {
query[URLParameter.FIELDS] = buildQueryFieldsForMany([
...(input[Parameter.FIELDS] ? [input[Parameter.FIELDS]] : []),
...(input[URLParameter.FIELDS] ? [input[URLParameter.FIELDS]] : []),
]);
query[URLParameter.FIELDS] = mergeQueryFields(
buildQueryFields(input[Parameter.FIELDS]),
buildQueryFields(input[URLParameter.FIELDS]),
);
}

if (
typeof input[Parameter.FILTERS] !== 'undefined' ||
typeof input[URLParameter.FILTERS] !== 'undefined'
) {
query[URLParameter.FILTERS] = buildQueryFiltersForMany([
...(input[Parameter.FILTERS] ? [input[Parameter.FILTERS]] : []),
...(input[URLParameter.FILTERS] ? [input[URLParameter.FILTERS]] : []),
]);
query[URLParameter.FILTERS] = mergeQueryFilters(
buildQueryFilters(input[Parameter.FILTERS]),
buildQueryFilters(input[URLParameter.FILTERS]),
);
}

if (
typeof input[Parameter.PAGINATION] !== 'undefined' ||
typeof input[URLParameter.PAGINATION] !== 'undefined'
) {
query[URLParameter.PAGINATION] = buildQueryPaginationForMany([
...(input[Parameter.PAGINATION] ? [input[Parameter.PAGINATION]] : []),
...(input[URLParameter.PAGINATION] ? [input[URLParameter.PAGINATION]] : []),
]);
query[URLParameter.PAGINATION] = mergeQueryPagination(
input[Parameter.PAGINATION],
input[URLParameter.PAGINATION],
);
}

if (
typeof input[Parameter.RELATIONS] !== 'undefined' ||
typeof input[URLParameter.RELATIONS] !== 'undefined'
) {
query[URLParameter.RELATIONS] = buildQueryRelationsForMany([
...(input[Parameter.RELATIONS] ? [input[Parameter.RELATIONS]] : []),
...(input[URLParameter.RELATIONS] ? [input[URLParameter.RELATIONS]] : []),
]);
query[URLParameter.RELATIONS] = mergeQueryRelations(
buildQueryRelations(input[Parameter.RELATIONS]),
buildQueryRelations(input[URLParameter.RELATIONS]),
);
}

if (
typeof input[Parameter.SORT] !== 'undefined' ||
typeof input[URLParameter.SORT] !== 'undefined'
) {
query[URLParameter.SORT] = buildQuerySortForMany([
...(input[Parameter.SORT] ? [input[Parameter.SORT]] : []),
...(input[URLParameter.SORT] ? [input[URLParameter.SORT]] : []),
]);
query[URLParameter.SORT] = mergeQuerySort(
buildQuerySort(input[Parameter.SORT]),
buildQuerySort(input[URLParameter.SORT]),
);
}

return buildURLQueryString(query);
Expand Down
69 changes: 40 additions & 29 deletions src/parameter/fields/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,50 @@
* view the LICENSE file that was distributed with this source code.
*/

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

export function buildQueryFieldsForMany<T>(
inputs: FieldsBuildInput<T>[],
): Record<string, any> | string | string[] {
let data: FieldsBuildInput<T>;

for (let i = 0; i < inputs.length; i++) {
if (data) {
const current = inputs[i];
if (typeof data === 'string' || typeof current === 'string') {
data = inputs[i];
} else {
data = mergeDeep(data, current);
}
} else {
data = inputs[i];
}
import { flattenToKeyPathArray, groupArrayByKeyPath } from '../../utils';

export function buildQueryFields<T>(
input?: FieldsBuildInput<T>,
) : Record<string, string[]> | string[] {
if (typeof input === 'undefined') {
return [];
}

return buildQueryFields(data);
const data = groupArrayByKeyPath(flattenToKeyPathArray(input));

const keys = Object.keys(data);
if (keys.length === 1) {
return data[keys[0]];
}

return data;
}

export function buildQueryFields<T>(
data: FieldsBuildInput<T>,
): Record<string, any> | string | string[] {
switch (true) {
case typeof data === 'string':
return data;
case Array.isArray(data):
return data;
default:
return flattenNestedObject(data as Record<string, any>);
export function mergeQueryFields<T>(
target: Record<string, string[]> | string[],
source: Record<string, string[]> | string[],
): Record<string, string[]> | string[] {
if (Array.isArray(target)) {
target = groupArrayByKeyPath(target);
}

if (Array.isArray(source)) {
source = groupArrayByKeyPath(source);
}

const merge = createMerger({
array: true,
arrayDistinct: true,
});

const data = merge({}, target, source);

const keys = Object.keys(data);
if (keys.length === 1) {
return data[keys[0]];
}

return data;
}
3 changes: 2 additions & 1 deletion src/parameter/fields/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export type FieldsBuildInput<T extends Record<string, any>> =
},
]
|
FieldWithOperator<NestedKeys<T>>[];
FieldWithOperator<NestedKeys<T>>[] |
FieldWithOperator<NestedKeys<T>>;

// -----------------------------------------------------------
// Parse
Expand Down
63 changes: 28 additions & 35 deletions src/parameter/filters/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,41 @@
* view the LICENSE file that was distributed with this source code.
*/

import { merge } from 'smob';
import { FiltersBuildInput } from './type';
import { FilterOperator } from './constants';
import { isFilterOperatorConfig } from './utils';
import { flattenNestedObject, mergeDeep } from '../../utils';
import { flattenNestedObject } from '../../utils';

export function buildQueryFiltersForMany<T>(
input: FiltersBuildInput<T>[],
) : Record<string, string> {
let data : FiltersBuildInput<T>;
for (let i = 0; i < input.length; i++) {
if (data) {
data = mergeDeep(data, input[i]);
} else {
data = input[i];
}
}

return buildQueryFilters(data);
}
const OperatorWeight = {
[FilterOperator.NEGATION]: 0,
[FilterOperator.LIKE]: 50,
[FilterOperator.LESS_THAN_EQUAL]: 150,
[FilterOperator.LESS_THAN]: 450,
[FilterOperator.MORE_THAN_EQUAL]: 1350,
[FilterOperator.MORE_THAN]: 4050,
[FilterOperator.IN]: 13105,
};

export function buildQueryFilters<T>(
data: FiltersBuildInput<T>,
) : Record<string, string> {
data?: FiltersBuildInput<T>,
) : Record<string, any> {
if (typeof data === 'undefined') {
return {};
}

return flattenNestedObject(data, {
transformer: (input, output, key) => {
if (typeof input === 'undefined') {
output[key] = null;

return undefined;
return true;
}

if (isFilterOperatorConfig(input)) {
input.value = transformValue(input.value);
if (typeof input.value === 'undefined') {
input.value = null;
}

if (Array.isArray(input.operator)) {
// merge operators
Expand All @@ -47,27 +49,18 @@ export function buildQueryFilters<T>(
}

output[key] = `${input.operator}${input.value}`;

return true;
}

return undefined;
},
});
}

const OperatorWeight = {
[FilterOperator.NEGATION]: 0,
[FilterOperator.LIKE]: 50,
[FilterOperator.LESS_THAN_EQUAL]: 150,
[FilterOperator.LESS_THAN]: 450,
[FilterOperator.MORE_THAN_EQUAL]: 1350,
[FilterOperator.MORE_THAN]: 4050,
[FilterOperator.IN]: 13105,
};

function transformValue<T>(value: T) : T | null {
if (typeof value === 'undefined') {
return null;
}

return value;
export function mergeQueryFilters<T>(
target?: Record<string, any>,
source?: Record<string, any>,
) : Record<string, any> {
return merge({}, target || {}, source || {});
}
2 changes: 1 addition & 1 deletion src/parameter/filters/utils/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function determineFilterOperatorLabelsByValue(input: string) : {
}

export function isFilterOperatorConfig(data: unknown) : data is FilterOperatorConfig<any> {
if (typeof data !== 'object') {
if (typeof data !== 'object' || data === null) {
return false;
}

Expand Down
20 changes: 5 additions & 15 deletions src/parameter/pagination/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,12 @@
* view the LICENSE file that was distributed with this source code.
*/

import { merge } from 'smob';
import { PaginationBuildInput } from './type';
import { mergeDeep } from '../../utils';

export function buildQueryPaginationForMany<T>(
inputs: PaginationBuildInput<T>[],
export function mergeQueryPagination<T>(
target?: PaginationBuildInput<T>,
source?: PaginationBuildInput<T>,
) : PaginationBuildInput<T> {
const inputSources = Array.isArray(inputs) ? inputs : [inputs];

let data : PaginationBuildInput<T>;
for (let i = 0; i < inputSources.length; i++) {
if (data) {
data = mergeDeep(data, inputSources[i]);
} else {
data = inputSources[i];
}
}

return data;
return merge({}, target || {}, source || {});
}
28 changes: 12 additions & 16 deletions src/parameter/relations/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,23 @@
* view the LICENSE file that was distributed with this source code.
*/

import { mergeArrays } from 'smob';
import { RelationsBuildInput } from './type';
import { flattenNestedObject, mergeDeep } from '../../utils';
import { flattenToKeyPathArray } from '../../utils';

export function buildQueryRelationsForMany<T>(
input: RelationsBuildInput<T>[],
export function buildQueryRelations<T>(
input?: RelationsBuildInput<T>,
) : string[] {
let data : RelationsBuildInput<T>;
for (let i = 0; i < input.length; i++) {
if (data) {
data = mergeDeep(data, input[i]);
} else {
data = input[i];
}
if (typeof input === 'undefined') {
return input;
}

return buildQueryRelations(data);
return flattenToKeyPathArray(input);
}

export function buildQueryRelations<T>(data: RelationsBuildInput<T>): string[] {
const properties: Record<string, boolean> = flattenNestedObject(data);
const keys: string[] = Object.keys(properties);

return Array.from(new Set(keys));
export function mergeQueryRelations<T>(
target?: string[],
source?: string[],
) : string[] {
return mergeArrays(target || [], source || [], true);
}
6 changes: 4 additions & 2 deletions src/parameter/relations/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import { Flatten, NestedResourceKeys, OnlyObject } from '../../type';
// -----------------------------------------------------------

export type RelationsBuildInput<T extends Record<string, any>> = {
[K in keyof T]?: T[K] extends OnlyObject<T[K]> ? RelationsBuildInput<Flatten<T[K]>> | boolean : never
};
[K in keyof T]?: Flatten<T[K]> extends OnlyObject<T[K]> ?
RelationsBuildInput<Flatten<T[K]>> | boolean :
never
} | NestedResourceKeys<T>[];

// -----------------------------------------------------------
// Parse
Expand Down
Loading

0 comments on commit 40d56c4

Please sign in to comment.