Skip to content

Commit

Permalink
Lint rule semi anti join not supported (#9)
Browse files Browse the repository at this point in the history
* just keep it

* implement no-semi-anti-join-supported eslint graphql rule

* final touchup

* add doc url

* more polish addressing pr comments

* format:fix touchup

* move ruleTest creation into shared.ts
  • Loading branch information
ben-zhang-at-salesforce authored May 24, 2024
1 parent 843a9e0 commit db1739d
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 21 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"url": "https://github.com/salesforce/eslint-plugin-lwc-mobile"
},
"scripts": {
"build": "tsc",
"clean": "npx rimraf dist reports",
"build": "npm run clean && tsc",
"format": "prettier --list-different \"**/*.{ts,js}\"",
"format:fix": "prettier --write \"**/*.{ts,js,json}\"",
"lint": "eslint src test",
Expand Down
15 changes: 11 additions & 4 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import type { ClassicConfig } from '@typescript-eslint/utils/ts-eslint';
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_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID } from '../rules/graphql/no-semi-anti-join-supported';
import { createScopedModuleRuleName } from '../util/createScopedModuleRuleName.js';

export = {
extends: ['./configs/base'],
rules: {
'@salesforce/lwc-mobile/apex-import': 'warn'
[createScopedModuleRuleName(APEX_IMPORT_RULE_ID)]: 'warn'
},
overrides: [
{
Expand All @@ -24,9 +30,10 @@ export = {
skipGraphQLConfig: true
},
rules: {
'@salesforce/lwc-mobile/offline-graphql-no-aggregate-query-supported': 'warn',
'@salesforce/lwc-mobile/offline-graphql-no-mutation-supported': 'warn',
'@salesforce/lwc-mobile/offline-graphql-unsupported-scope': 'warn'
[createScopedModuleRuleName(NO_MUTATION_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID)]: 'warn',
[createScopedModuleRuleName(UNSUPPORTED_SCOPE_RULE_ID)]: 'warn',
[createScopedModuleRuleName(NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID)]: 'warn'
}
}
]
Expand Down
129 changes: 129 additions & 0 deletions src/rules/graphql/no-semi-anti-join-supported.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { GraphQLESLintRule, GraphQLESLintRuleContext } from '@graphql-eslint/eslint-plugin';
import getDocUrl from '../../util/getDocUrl';

export const NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID = 'offline-graphql-no-semi-anti-join-supported';

export const rule: GraphQLESLintRule = {
meta: {
type: 'problem',
hasSuggestions: false,
docs: {
description: 'Semi and anti join are not supported for mobile offline',
category: 'Operations',
recommended: true,
url: getDocUrl(NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID),
examples: [
{
title: 'Correct',
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`
},
{
title: 'Incorrect',
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account (where: {
Id: { inq: {
Opportunity: {
StageName: { eq: "Closed Won" } },
ApiName:"AccountId"
}
}
}) {
edges {
node {
Id
Name { value }
}
}
}
}
}
}
}
`
},
{
title: 'Incorrect',
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account (where: {
Id: { ninq: {
Opportunity: {
StageName: { eq: "Closed Won" } },
ApiName:"AccountId"
}
}
}) {
edges {
node {
Id
Name { value }
}
}
}
}
}
}
}
`
}
]
},
messages: {
[NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID]:
'Offline GraphQL: "{{joinType}}" join is not supported for mobile offline.'
},
schema: []
},

create(context: GraphQLESLintRuleContext) {
return {
ObjectField(node) {
if (node.name.value === 'inq') {
context.report({
node: node.name,
messageId: NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID,
data: {
joinType: 'Semi'
}
});
} else if (node.name.value === 'ninq') {
context.report({
node: node.name,
messageId: NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID,
data: {
joinType: 'Anti'
}
});
}
}
};
}
};
3 changes: 3 additions & 0 deletions src/util/createScopedModuleRuleName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function createScopedModuleRuleName(ruleName: string): string {
return `@salesforce/lwc-mobile/${ruleName}`;
}
3 changes: 2 additions & 1 deletion test/rules/apex/apex-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import { RuleTester } from '@typescript-eslint/rule-tester';

import { rule, APEX_IMPORT_RULE_ID } from '../../../src/rules/apex/apex-import';
import { createScopedModuleRuleName } from '../../../src/util/createScopedModuleRuleName';

const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser'
});

ruleTester.run('@salesforce/lwc-mobile/apex/apex-import', rule, {
ruleTester.run(createScopedModuleRuleName(APEX_IMPORT_RULE_ID), rule, {
valid: [
{
code: `
Expand Down
8 changes: 3 additions & 5 deletions test/rules/graphql/no-aggregate-query-supported.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import {
rule,
NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-aggregate-query-supported';
import { createScopedModuleRuleName } from '../../../src/util/createScopedModuleRuleName';

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

const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

ruleTester.run('@salesforce/lwc-mobile/no-aggregate-query-supported', rule as any, {
ruleTester.run(createScopedModuleRuleName(NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID), rule as any, {
valid: [
{
code: /* GraphQL */ `
Expand Down
8 changes: 3 additions & 5 deletions test/rules/graphql/no-mutation-supported.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import {
rule,
NO_MUTATION_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-mutation-supported';
import { createScopedModuleRuleName } from '../../../src/util/createScopedModuleRuleName';

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

const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

ruleTester.run('@salesforce/lwc-mobile/no-mutation-supported', rule as any, {
ruleTester.run(createScopedModuleRuleName(NO_MUTATION_SUPPORTED_RULE_ID), rule as any, {
valid: [
{
code: /* GraphQL */ `
Expand Down
108 changes: 108 additions & 0 deletions test/rules/graphql/no-semi-anti-join-supported.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
rule,
NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-semi-anti-join-supported';
import { createScopedModuleRuleName } from '../../../src/util/createScopedModuleRuleName';

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

ruleTester.run(createScopedModuleRuleName(NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID), rule as any, {
valid: [
{
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`
}
],
invalid: [
{
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account(
where: {
Id: {
inq: {
Opportunity: { StageName: { eq: "Closed Won" } }
ApiName: "AccountId"
}
}
}
) {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`,
errors: [
{
messageId: NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID,
data: {
joinType: 'Semi'
}
}
]
},
{
code: /* GraphQL */ `
query AccountExample {
uiapi {
query {
Account(
where: {
Id: {
ninq: {
Opportunity: { StageName: { eq: "Closed Won" } }
ApiName: "AccountId"
}
}
}
) {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`,
errors: [
{
messageId: NO_SEMI_ANTI_JOIN_SUPPORTED_RULE_ID,
data: {
joinType: 'Anti'
}
}
]
}
]
});
5 changes: 1 addition & 4 deletions test/rules/graphql/unsupported-scope.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import {
rule,
SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY,
OTHER_UNSUPPORTED_SCOPE
} from '../../../src/rules/graphql/unsupported-scope';
import { RULE_TESTER_CONFIG } from '../../shared';

const ruleTester = new RuleTester(RULE_TESTER_CONFIG);
import { ruleTester } from '../../shared';

ruleTester.run('@salesforce/lwc-mobile/offline-graphql-unsupported-scope', rule as any, {
valid: [
Expand Down
6 changes: 5 additions & 1 deletion test/shared.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export const RULE_TESTER_CONFIG = {
import { RuleTester } from '@typescript-eslint/rule-tester';

const RULE_TESTER_CONFIG = {
parser: '@graphql-eslint/eslint-plugin',
parserOptions: {
graphQLConfig: {}
}
};

export const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

0 comments on commit db1739d

Please sign in to comment.