Skip to content

Commit

Permalink
feat: custom fields on server-side [CU-xhcmx-63] (#161)
Browse files Browse the repository at this point in the history
* feat: custom fields on server-side

* feat: graphql for custom comments field
  • Loading branch information
CodeVoyager authored Nov 8, 2022
1 parent 3d8b995 commit a284d59
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 14 deletions.
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defaults as tsjPreset } from "ts-jest/presets";

const config: Config.InitialOptions = {
name: "Unit test",
testMatch: ["**/__tests__/?(*.)+(spec|test).ts"],
testMatch: ["**/__tests__/?(*.)+(spec|test).(t|j)s"],
transform: {
...tsjPreset.transform,
},
Expand Down
4 changes: 2 additions & 2 deletions server/controllers/utils/__tests__/functions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ describe("Test Comments controller functions utils", () => {
describe("Errors serialization", () => {
test("Should throw error PluginError", () => {
try {
throw new PluginError(400, "Error message", {
throw new PluginError.default(400, "Error message", {
content: {
param: "Parameter value",
},
});
} catch (e) {
expect(e).toBeInstanceOf(PluginError);
expect(e).toBeInstanceOf(PluginError.default);
expect(e).toHaveProperty("status", 400);
expect(e).toHaveProperty("name", "Strapi:Plugin:Comments");
expect(e).toHaveProperty("message", "Error message");
Expand Down
68 changes: 66 additions & 2 deletions server/controllers/utils/__tests__/parsers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ describe("Test Comments controller parsers utils", () => {
const fields = ["content"];

test("Should contain default property 'populate'", () => {
expect(flatInput()).toHaveProperty(
expect(flatInput({ query: {} })).toHaveProperty(
["populate", "threadOf", "populate", "authorUser"],
true
);
});

test("Should assign relation to 'query'", () => {
expect(flatInput({ relation })).toHaveProperty(
expect(flatInput({ relation, query: {} })).toHaveProperty(
["query", "related"],
relation
);
Expand Down Expand Up @@ -159,5 +159,69 @@ describe("Test Comments controller parsers utils", () => {
pagination.pageSize
);
});

test("Should assign filtering by comments status", () => {
const filterByValue = "APPROVED";
const result = flatInput({
relation,
query: {
...query,
filterBy: "APPROVAL_STATUS",
filterByValue,
},
sort,
fields,
});

expect(result).toHaveProperty(["query", "approvalStatus"], filterByValue);
expect(() => {
flatInput({
relation,
query: {
filterBy: "APPROVAL_STATUS",
},
sort,
fields,
});
}).toThrow();
});

test("Should assign filtering by creation date", () => {
const filterByValue = new Date().toUTCString();
const result = flatInput({
relation,
query: {
...query,
filterBy: "DATE_CREATED",
filterByValue,
},
sort,
fields,
});

expect(result).toHaveProperty(["query", "createdAt", "$between", 0]);
expect(result).toHaveProperty(["query", "createdAt", "$between", 1]);
expect(() => {
flatInput({
relation,
query: {
filterBy: "DATE_CREATED",
},
sort,
fields,
});
}).toThrow();
expect(() => {
flatInput({
relation,
query: {
filterByValue: "X",
filterBy: "DATE_CREATED",
},
sort,
fields,
});
}).toThrow();
});
});
});
28 changes: 27 additions & 1 deletion server/controllers/utils/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { OnlyStrings } from "strapi-typed";
import { FlatInput, ToBeFixed } from "../../../types";
import PluginError from "../../utils/error";
import { assertNotEmpty } from "../../utils/functions";

export const flatInput = <T, TKeys = keyof T>(
payload: FlatInput<OnlyStrings<TKeys>>
) => {
const { relation, query, sort, pagination, fields } = payload;

const { populate = {}, ...restQuery } = query;
const { populate = {}, filterBy, filterByValue, ...restQuery } = query;
const filters = restQuery?.filters || restQuery;
const orOperator = (filters?.$or || []).filter(
(_: ToBeFixed) => !Object.keys(_).includes("removed")
Expand Down Expand Up @@ -42,6 +44,30 @@ export const flatInput = <T, TKeys = keyof T>(
};
}

if (filterBy === "DATE_CREATED") {
const date = new Date(filterByValue);

if (!filterByValue || Number.isNaN(+date)) {
throw new PluginError(400, 'Invalid date specified in "filterByValue"');
}

const start = date.setHours(0, 0, 0, 0);
const end = date.setHours(23, 59, 59, 999);

filters.createdAt = {
$between: [start, end],
};
}

if (filterBy === "APPROVAL_STATUS") {
assertNotEmpty(
filterByValue,
new PluginError(400, 'Empty "filterByValue" parameter')
);

filters.approvalStatus = filterByValue;
}

return {
query: {
...filters,
Expand Down
4 changes: 4 additions & 0 deletions server/graphql/queries/findAllInHierarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ export = ({ strapi, nexus }: StrapiGraphQLContext) => {
const { nonNull, list, stringArg } = nexus;
const { service: getService } = strapi.plugin("graphql");
const { args } = getService("internals");
const {
naming: { getFiltersInputTypeName },
} = getService("utils");

return {
type: nonNull(list("CommentNested")),
args: {
relation: nonNull(stringArg()),
sort: args.SortArg,
filters: getFiltersInputTypeName(contentType),
},
// @ts-ignore
async resolve(obj: Object, args: findAllInHierarchyProps) {
Expand Down
46 changes: 40 additions & 6 deletions server/services/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { IServiceGraphQL, ToBeFixed } from "../../types";

import { has, propEq, isNil, isDate, isObject } from "lodash/fp";
import { inputObjectType } from "nexus";
import { assertNotEmpty } from "../utils/functions";

const virtualScalarAttributes = ["id"];
const customFields = ["filterBy", "filterByValue"];

export = ({ strapi }: StrapiContext): IServiceGraphQL => {
const { service: getService } = strapi.plugin("graphql");
Expand Down Expand Up @@ -65,6 +67,9 @@ export = ({ strapi }: StrapiContext): IServiceGraphQL => {
for (const operator of rootLevelOperators()) {
operator.add(t, filtersTypeName);
}

t.field("filterBy", { type: "String" });
t.field("filterByValue", { type: "String" });
},
});
};
Expand Down Expand Up @@ -132,7 +137,7 @@ export = ({ strapi }: StrapiContext): IServiceGraphQL => {
};

const graphQLFiltersToStrapiQuery = (
filters: ToBeFixed,
queryFilters: ToBeFixed,
contentType: ToBeFixed = {}
): Array<ToBeFixed> | ToBeFixed => {
const { isStrapiScalar, isMedia, isRelation } =
Expand All @@ -142,18 +147,23 @@ export = ({ strapi }: StrapiContext): IServiceGraphQL => {
const ROOT_LEVEL_OPERATORS = [operators.and, operators.or, operators.not];

// Handle unwanted scenario where there is no filters defined
if (isNil(filters)) {
if (isNil(queryFilters)) {
return {};
}

// If filters is a collection, then apply the transformation to every item of the list
if (Array.isArray(filters)) {
return filters.map((filtersItem) =>
graphQLFiltersToStrapiQuery(filtersItem, contentType)
);
if (Array.isArray(queryFilters)) {
return queryFilters.reduce((acc, filtersItem) => {
if (!customFields.includes(filtersItem)) {
acc.push(graphQLFiltersToStrapiQuery(filtersItem, contentType));
}

return acc;
});
}

const resultMap: ToBeFixed = {};
const { filterBy, filterByValue, ...filters } = queryFilters;
const { attributes } = contentType;

const isAttribute = (attributeName: string): boolean => {
Expand Down Expand Up @@ -208,6 +218,30 @@ export = ({ strapi }: StrapiContext): IServiceGraphQL => {
}
}

if (filterBy === "DATE_CREATED") {
const date = new Date(filterByValue);

if (!filterByValue || Number.isNaN(+date)) {
throw new Error('Invalid date specified in "filterByValue"');
}

const start = date.setHours(0, 0, 0, 0);
const end = date.setHours(23, 59, 59, 999);

resultMap.createdAt = {
$between: [start, end],
};
}

if (filterBy === "APPROVAL_STATUS") {
assertNotEmpty(
filterByValue,
new Error('Empty "filterByValue" parameter')
);

resultMap.approvalStatus = filterByValue;
}

return resultMap;
};

Expand Down
4 changes: 2 additions & 2 deletions server/services/utils/__tests__/functions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe("Test service functions utils", () => {
try {
resolveUserContextError({ id: 1 });
} catch (e) {
expect(e).toBeInstanceOf(PluginError);
expect(e).toBeInstanceOf(PluginError.default);
expect(e).toHaveProperty("status", 401);
}
});
Expand All @@ -36,7 +36,7 @@ describe("Test service functions utils", () => {
try {
resolveUserContextError();
} catch (e) {
expect(e).toBeInstanceOf(PluginError);
expect(e).toBeInstanceOf(PluginError.default);
expect(e).toHaveProperty("status", 403);
}
});
Expand Down

0 comments on commit a284d59

Please sign in to comment.