Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lint rule offline supported scope #8

Merged
merged 10 commits into from
May 23, 2024
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const config: Config = {
testMatch: ['<rootDir>/test/**/*.ts'],
moduleFileExtensions: ['ts', 'js', 'json'],
testResultsProcessor: 'jest-sonar-reporter',
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/lib/'],
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/lib/', '<rootDir>/test/shared.ts'],
moduleDirectories: ['node_modules'],
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!src/index.ts', '!src/configs/*.ts'],
Expand Down
7 changes: 5 additions & 2 deletions src/configs/recommended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import type { ClassicConfig } from '@typescript-eslint/utils/ts-eslint';

export = {
extends: ['./configs/base'],
rules: {
'@salesforce/lwc-mobile/apex-import': 'warn'
},
overrides: [
{
files: ['*.js'],
Expand All @@ -21,9 +24,9 @@ export = {
skipGraphQLConfig: true
},
rules: {
'@salesforce/lwc-mobile/apex-import': 'warn',
'@salesforce/lwc-mobile/offline-graphql-no-aggregate-query-supported': 'warn',
'@salesforce/lwc-mobile/offline-graphql-no-mutation-supported': 'warn',
'@salesforce/lwc-mobile/offline-graphql-no-aggregate-query-supported': 'warn'
'@salesforce/lwc-mobile/offline-graphql-unsupported-scope': 'warn'
sfdctaka marked this conversation as resolved.
Show resolved Hide resolved
}
}
]
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import {
rule as mutationNotSupported
} from './rules/graphql/no-mutation-supported.js';

import {
UNSUPPORTED_SCOPE_RULE_ID,
rule as unsupportedScope
} from './rules/graphql/unsupported-scope.js';

import { name, version } from '../package.json';

export = {
Expand All @@ -33,7 +38,8 @@ export = {
rules: {
'enforce-foo-bar': enforceFooBar,
[APEX_IMPORT_RULE_ID]: apexImport,
[NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID]: aggregateQueryNotSupported,
[NO_MUTATION_SUPPORTED_RULE_ID]: mutationNotSupported,
[NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID]: aggregateQueryNotSupported
[UNSUPPORTED_SCOPE_RULE_ID]: unsupportedScope
}
};
114 changes: 114 additions & 0 deletions src/rules/graphql/unsupported-scope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { GraphQLESLintRule, GraphQLESLintRuleContext } from '@graphql-eslint/eslint-plugin';
import { Kind } from 'graphql';

export const UNSUPPORTED_SCOPE_RULE_ID = 'offline-graphql-unsupported-scope';

export const SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY = 'ASSIGNED_TO_ME__SERVICEAPPOINTMENT_ONLY';
export const OTHER_UNSUPPORTED_SCOPE = 'OTHER_UNSUPPORTED_SCOPE';

import getDocUrl from '../../util/getDocUrl';

// key is scope name, value is the array of supported entities. Empty array means that all entities are supported.
const supportedScopes: Record<string, string[]> = {
haifeng-li-at-salesforce marked this conversation as resolved.
Show resolved Hide resolved
MINE: [],
ASSIGNEDTOME: ['ServiceAppointment']
};
export const rule: GraphQLESLintRule = {
meta: {
type: 'problem',
docs: {
category: 'Operations',
description: `For mobile offline use cases, scope "MINE" is supported and scope "ASSIGNEDTOME" is only supported for ServiceAppointment . All other scopes like TEAM, QUEUE_OWNED and USER_OWNED are not supported `,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: `For mobile offline use cases, scope "MINE" is supported and scope "ASSIGNEDTOME" is only supported for ServiceAppointment . All other scopes like TEAM, QUEUE_OWNED and USER_OWNED are not supported `,
description: `For mobile offline use cases, scope "MINE" is supported and scope "ASSIGNEDTOME" is only supported for ServiceAppointment. All other scopes like TEAM, QUEUE_OWNED and USER_OWNED are not supported.`,

url: getDocUrl(UNSUPPORTED_SCOPE_RULE_ID),
recommended: true,
examples: [
{
title: 'Incorrect',
code: /* GraphQL */ `
query scopeQuery {
uiapi {
query {
Case(scope: EVERYTHING) {
edges {
node {
Id
}
}
}
}
}
}
`
},
{
title: 'Incorrect',
code: /* GraphQL */ `
query assignedtomeQuery {
uiapi {
query {
Case(scope: ASSIGNEDTOME) {
edges {
node {
Id
}
}
}
}
}
}
`
}
]
},
messages: {
[SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY]:
'Offline GraphQL: Scope "{{scopeName}}" is only supported for the "{{supportedEntities}}" entity, for mobile offline use cases',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'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.'
},
schema: []
},

create(context: GraphQLESLintRuleContext) {
return {
Argument(node) {
if (node.name.value === 'scope' && node.value.kind === Kind.ENUM) {
const scopeName = node.value.value;
if (supportedScopes[scopeName] === undefined) {
context.report({
messageId: OTHER_UNSUPPORTED_SCOPE,
data: {
scopeName
},
loc: {
start: node.loc.start,
end: node.value.loc.end
}
});
} else {
const entities = supportedScopes[scopeName];
if (entities.length > 0) {
const entityNode = node.parent as any;
haifeng-li-at-salesforce marked this conversation as resolved.
Show resolved Hide resolved
if (entityNode.name.kind === Kind.NAME) {
const entityName = entityNode.name.value;
if (!entities.includes(entityName)) {
context.report({
messageId: SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY,
loc: {
start: node.loc.start,
end: node.value.loc.end
},
data: {
scopeName,
supportedEntities: entities.join(', ')
}
});
}
}
}
}
}
}
};
}
};
9 changes: 3 additions & 6 deletions test/rules/graphql/no-aggregate-query-supported.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import {
NO_AGGREGATE_QUERY_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-aggregate-query-supported';

const ruleTester = new RuleTester({
parser: '@graphql-eslint/eslint-plugin',
parserOptions: {
graphQLConfig: {}
}
});
import { RULE_TESTER_CONFIG } from '../../shared';

const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

ruleTester.run('@salesforce/lwc-mobile/no-aggregate-query-supported', rule as any, {
valid: [
Expand Down
9 changes: 3 additions & 6 deletions test/rules/graphql/no-mutation-supported.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import {
NO_MUTATION_SUPPORTED_RULE_ID
} from '../../../src/rules/graphql/no-mutation-supported';

const ruleTester = new RuleTester({
parser: '@graphql-eslint/eslint-plugin',
parserOptions: {
graphQLConfig: {}
}
});
import { RULE_TESTER_CONFIG } from '../../shared';

const ruleTester = new RuleTester(RULE_TESTER_CONFIG);

ruleTester.run('@salesforce/lwc-mobile/no-mutation-supported', rule as any, {
sfdctaka marked this conversation as resolved.
Show resolved Hide resolved
valid: [
Expand Down
90 changes: 90 additions & 0 deletions test/rules/graphql/unsupported-scope.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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);

ruleTester.run('@salesforce/lwc-mobile/offline-graphql-unsupported-scope', rule as any, {
valid: [
{
code: /* GraphQL */ `
query scopeQuery {
uiapi {
query {
ServiceAppointment(first: 20, scope: ASSIGNEDTOME) {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`
}
],
invalid: [
{
code: /* GraphQL */ `
query scopeQuery {
uiapi {
query {
Case(scope: EVERYTHING) {
edges {
node {
Id
}
}
}
}
}
}
`,
errors: [
{
messageId: OTHER_UNSUPPORTED_SCOPE,
data: {
scopeName: 'EVERYTHING'
}
}
]
},
{
code: /* GraphQL */ `
query scopeQuery {
uiapi {
query {
Case(first: 20, scope: ASSIGNEDTOME) {
edges {
node {
Id
Name {
value
}
}
}
}
}
}
}
`,
errors: [
{
messageId: SCOPE_SUPPORTED_FOR_CERTAIN_ENTITIES_ONLY,
data: {
scopeName: 'ASSIGNEDTOME',
supportedEntities: 'ServiceAppointment'
}
}
]
}
]
});
6 changes: 6 additions & 0 deletions test/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const RULE_TESTER_CONFIG = {
parser: '@graphql-eslint/eslint-plugin',
parserOptions: {
graphQLConfig: {}
}
};
Loading