Skip to content

Commit

Permalink
Merge pull request #10 from salesforce/lintRuleUnsupportedDateFilter
Browse files Browse the repository at this point in the history
Lint rule unsupported date filter
  • Loading branch information
haifeng-li-at-salesforce committed May 29, 2024
2 parents c780e5e + 298de32 commit ea70350
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 7 deletions.
8 changes: 5 additions & 3 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { APEX_IMPORT_RULE_ID } from '../rules/apex/apex-import.js';
import { NO_MUTATION_SUPPORTED_RULE_ID } from '../rules/graphql/no-mutation-supported';
import { NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID } from '../rules/graphql/no-aggregate-query-supported';
import { UNSUPPORTED_SCOPE_RULE_ID } from '../rules/graphql/unsupported-scope.js';
import { NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID } from '../rules/graphql/no-fiscal-date-filtering-supported.js';
import { NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID } from '../rules/graphql/no-semi-anti-join-supported';
import { NO_MORE_THAN_1_PARENT_RECORD_RULE_ID } from '../rules/graphql/no-more-than-1-parent-record.js';
import { NO_MORE_THAN_3_CHILD_ENTITIES_RULE_ID } from '../rules/graphql/no-more-than-3-child-entities.js';
Expand All @@ -33,13 +34,14 @@ export = {
skipGraphQLConfig: true
},
rules: {
[createScopedModuleRuleName(NO_MUTATION_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(UNSUPPORTED_SCOPE_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_MUTATION_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_MORE_THAN_1_PARENT_RECORD_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_MORE_THAN_3_CHILD_ENTITIES_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_MORE_THAN_3_ROOT_ENTITIES_RULE_ID)]: 'warn'
[createScopedModuleRuleName(NO_MORE_THAN_3_ROOT_ENTITIES_RULE_ID)]: 'warn',
[createScopedModuleRuleName(UNSUPPORTED_SCOPE_RULE_ID)]: 'warn'
}
}
]
Expand Down
12 changes: 9 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import {
rule as unsupportedScope
} from './rules/graphql/unsupported-scope.js';

import {
NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID,
rule as fiscalDataFilteringNotSupported
} from './rules/graphql/no-fiscal-date-filtering-supported.js';

import {
NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID,
rule as noSemiAntiJoinSupported
Expand Down Expand Up @@ -57,11 +62,12 @@ export = {
rules: {
[APEX_IMPORT_RULE_ID]: apexImport,
[NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID]: aggregateQueryNotSupported,
[NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID]: fiscalDataFilteringNotSupported,
[NO_MUTATION_SUPPORTED_RULE_ID]: mutationNotSupported,
[UNSUPPORTED_SCOPE_RULE_ID]: unsupportedScope,
[NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID]: noSemiAntiJoinSupported,
[NO_MORE_THAN_1_PARENT_RECORD_RULE_ID]: noMoreThan1ParentRecord,
[NO_MORE_THAN_3_CHILD_ENTITIES_RULE_ID]: noMoreThan3ChildEntities,
[NO_MORE_THAN_3_ROOT_ENTITIES_RULE_ID]: noMoreThan3RootEntities
[NO_MORE_THAN_3_ROOT_ENTITIES_RULE_ID]: noMoreThan3RootEntities,
[NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID]: noSemiAntiJoinSupported,
[UNSUPPORTED_SCOPE_RULE_ID]: unsupportedScope
}
};
153 changes: 153 additions & 0 deletions src/rules/graphql/no-fiscal-date-filtering-supported.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { Kind } from 'graphql';
import { GraphQLESLintRule, GraphQLESLintRuleContext } from '@graphql-eslint/eslint-plugin';
import getDocUrl from '../../util/getDocUrl';
export const NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID =
'offline-graphql-no-fiscal-date-filter-supported';

type NodeWithParent = {
kind: string;
parent: NodeWithParent;
};

export const rule: GraphQLESLintRule = {
meta: {
type: 'problem',
docs: {
description: 'fiscal date literals/ranges are not supported offline',
category: 'Operations',
url: getDocUrl(NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID),
recommended: true,
examples: [
{
title: 'Correct',
code: /* GraphQL */ `
{
uiapi {
query {
Account(
where: {
LastActivityDate: {
eq: { literal: { THIS_YEAR } }
}
}
) {
edges {
node {
Id
}
}
}
}
}
}
`
},
{
title: 'InCorrect',
code: /* GraphQL */ `
{
uiapi {
query {
Account(
where: {
LastActivityDate: { eq: { literal: THIS_FISCAL_YEAR } }
}
) {
edges {
node {
Id
}
}
}
}
}
}
`
},
{
title: 'InCorrect',
code: /* GraphQL */ `
{
uiapi {
query {
Account(
where: {
LastActivityDate: {
eq: { range: { last_n_fiscal_years: 1 } }
}
}
) {
edges {
node {
Id
}
}
}
}
}
}
`
}
]
},
messages: {
[NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID]:
'Offline GraphQL: Date {{filterType}} "{{filterName}}" is not supported offline.'
},
schema: []
},
create(context: GraphQLESLintRuleContext) {
return {
ObjectField(node) {
if (node.kind == Kind.OBJECT_FIELD && node.name.kind === Kind.NAME) {
// Handles literal. Example: where : {LastAcitivtyDate: {eq: {literal : LAST_FISCAL_YEAR }}}.
if (
node.name.value === 'literal' &&
node.value.kind === Kind.ENUM &&
node.value.value.indexOf('_FISCAL_') > 0 &&
isInFilter(node as NodeWithParent)
) {
context.report({
messageId: NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID,
node: node.value,
data: {
filterType: 'literal',
filterName: node.value.value
}
});
return;
}
//Handles ranges. Example: where : {LastAcitivtyDate: {eq: {range: {last_n_fiscal_years: 1 }}}}.
if (
node.name.value === 'range' &&
node.value.kind === Kind.OBJECT &&
node.value.fields.length > 0
) {
const rangeObjectField = node.value.fields[0];
// Checks if it is a fiscal date filter, for example 'last_n_fiscal_quarters', 'n_fiscal_years_ago'.
if (
rangeObjectField.name.value.indexOf('_fiscal_') > 0 &&
isInFilter(rangeObjectField as NodeWithParent)
) {
context.report({
messageId: NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID,
node: rangeObjectField,
data: {
filterType: 'range',
filterName: rangeObjectField.name.value
}
});
}
}
}
}
};
}
};

function isInFilter(node: NodeWithParent): boolean {
if (node.kind === Kind.ARGUMENT) {
return true;
}
return node.parent === null ? false : isInFilter(node.parent);
}
2 changes: 1 addition & 1 deletion src/rules/graphql/unsupported-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const rule: GraphQLESLintRule = {
},
messages: {
[SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY]:
'Offline GraphQL: Scope "{{scopeName}}" is only supported for the "{{supportedEntities}}" entity, for mobile offline use cases',
'Offline GraphQL: Scope "{{scopeName}}" is only supported for the "{{supportedEntities}}" entity for mobile offline use cases',
[OTHER_UNSUPPORTED_SCOPE]:
'Offline GraphQL: Scope "{{scopeName}}" is unsupported for mobile offline use cases.'
},
Expand Down
88 changes: 88 additions & 0 deletions test/rules/graphql/no-fiscal-date-filtering-supported.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {
rule,
NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-fiscal-date-filtering-supported';

import { ruleTester } from '../../shared';

ruleTester.run('@salesforce/lwc-mobile/no-fiscal-date-filtering-supported', rule as any, {
valid: [
{
code: /* GraphQL */ `
{
uiapi {
query {
Account(where: { LastActivityDate: { eq: { literal: THIS_YEAR } } }) {
edges {
node {
Id
}
}
}
}
}
}
`
}
],
invalid: [
{
code: /* GraphQL */ `
{
uiapi {
query {
Account(
where: {
LastActivityDate: { eq: { range: { last_n_fiscal_years: 1 } } }
}
) {
edges {
node {
Id
}
}
}
}
}
}
`,
errors: [
{
messageId: NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID,
data: {
filterType: 'range',
filterName: 'last_n_fiscal_years'
}
}
]
},
{
code: /* GraphQL */ `
{
uiapi {
query {
Account(
where: { LastActivityDate: { eq: { literal: THIS_FISCAL_YEAR } } }
) {
edges {
node {
Id
}
}
}
}
}
}
`,
errors: [
{
messageId: NO_FISCAL_DATE_FILTER_SUPPORTED_RULE_ID,
data: {
filterType: 'literal',
filterName: 'THIS_FISCAL_YEAR'
}
}
]
}
]
});

0 comments on commit ea70350

Please sign in to comment.