Skip to content

Commit

Permalink
update files and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
khaliqgant committed Sep 19, 2024
1 parent b039cbc commit 423956e
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 49 deletions.
57 changes: 42 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@nangohq/eslint-plugin-nango-custom-integrations-linting",
"name": "@nangohq/eslint-plugin-custom-integrations-linting",
"version": "1.0.0",
"description": "Nango custom integrations ESLint plugin",
"main": "dist/index.js",
Expand All @@ -15,10 +15,10 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/types": "^7.22.5",
"@types/eslint": "^8.40.0",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"@typescript-eslint/types": "^8.6.0",
"eslint": "^8.42.0",
"typescript": "^5.1.3",
"vitest": "^0.31.4"
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import noObjectCasting from './rules/no-object-casting';
import noValueModification from './rules/no-value-modification';
import noWhileTrue from './rules/no-while-true'
import proxyCallRetries from './rules/proxy-call-retries';
import enforceProxyConfigurationType from './rules/enforce-proxy-configuration-type';
import typeExternalApiResponses from './rules/type-external-api-responses';

export = {
rules: {
'enforce-proxy-configuration-type': enforceProxyConfigurationType,
'no-console-log': noConsoleLog,
'no-object-casting': noObjectCasting,
'no-value-modification': noValueModification,
Expand Down
113 changes: 113 additions & 0 deletions src/rules/enforce-proxy-configuration-type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { RuleTester } from 'eslint';
import { describe, it } from 'vitest';
import enforceProxyConfigurationType from './enforce-proxy-configuration-type';

const ruleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
});

describe('enforce-proxy-configuration-type', () => {
it('should pass valid cases and fail invalid cases', () => {
ruleTester.run('enforce-proxy-configuration-type', enforceProxyConfigurationType, {
valid: [
{
code: `
const config: ProxyConfiguration = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.post(config);
`,
},
{
code: `
let config: ProxyConfiguration;
config = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.get(config);
`,
},
{
code: `
const res = await nango.put({ endpoint: 'api.example.com', data: {} });
`,
},
],
invalid: [
{
code: `
const config = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.post(config);
`,
errors: [{ message: 'Configuration object for Nango API calls should be typed as ProxyConfiguration' }],
output: `
const config: ProxyConfiguration = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.post(config);
`,
},
{
code: `
let config = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.get(config);
`,
errors: [{ message: 'Configuration object for Nango API calls should be typed as ProxyConfiguration' }],
output: `
let config: ProxyConfiguration = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.get(config);
`,
},
{
code: `
var config = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.proxy(config);
`,
errors: [{ message: 'Configuration object for Nango API calls should be typed as ProxyConfiguration' }],
output: `
var config: ProxyConfiguration = {
endpoint: 'api.xro/2.0/Contacts',
headers: { 'xero-tenant-id': tenant_id },
params: { summarizeErrors: 'false' },
data: { Contacts: input.map(toXeroContact) }
};
const res = await nango.proxy(config);
`,
},
],
});
});
});
77 changes: 77 additions & 0 deletions src/rules/enforce-proxy-configuration-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Rule } from 'eslint';
import { Node, CallExpression, Identifier, VariableDeclarator } from 'estree';

const enforceProxyConfigurationType: Rule.RuleModule = {
meta: {
type: 'suggestion',
docs: {
description: 'Enforce ProxyConfiguration type for Nango API call configurations',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
schema: [],
},
create(context: Rule.RuleContext) {
const sourceCode = context.getSourceCode();

return {
CallExpression(node: Node) {
if (isNangoApiCall(node)) {
const options = node.arguments[0];

if (options && options.type === 'Identifier') {
const scope = sourceCode.getScope(node);
const variable = scope.variables.find(v => v.name === options.name);
if (variable && variable.defs[0] && variable.defs[0].node.type === 'VariableDeclarator') {
const declarator = variable.defs[0].node;
if (!hasProxyConfigurationType(declarator, context)) {
context.report({
node: declarator,
message: 'Configuration object for Nango API calls should be typed as ProxyConfiguration',
fix(fixer) {
const declarationToken = sourceCode.getFirstToken(declarator.parent);
if (declarationToken && (declarationToken.value === 'const' || declarationToken.value === 'let' || declarationToken.value === 'var')) {
return fixer.insertTextAfter(declarator.id, ': ProxyConfiguration');
}
return null;
}
});
}
}
}
}
},
};
},
};

function isNangoApiCall(node: Node): node is CallExpression {
return (
node.type === 'CallExpression' &&
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'nango' &&
node.callee.property.type === 'Identifier' &&
['get', 'post', 'put', 'patch', 'delete', 'proxy'].includes(node.callee.property.name)
);
}

function hasProxyConfigurationType(node: VariableDeclarator, context: Rule.RuleContext): boolean {
const sourceCode = context.getSourceCode();
const idToken = sourceCode.getFirstToken(node.id);
if (!idToken) return false;

const nextToken = sourceCode.getTokenAfter(idToken);
if (!nextToken) return false;

const tokenAfterColon = sourceCode.getTokenAfter(nextToken);

return (
nextToken.type === 'Punctuator' &&
nextToken.value === ':' &&
tokenAfterColon?.value === 'ProxyConfiguration'
);
}

export default enforceProxyConfigurationType;
5 changes: 2 additions & 3 deletions src/rules/no-object-casting.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Rule } from 'eslint';
import { TSAsExpression, TSTypeAssertion } from '@typescript-eslint/types/dist/ast-spec';

const noObjectCasting: Rule.RuleModule = {
meta: {
Expand All @@ -13,13 +12,13 @@ const noObjectCasting: Rule.RuleModule = {
},
create(context: Rule.RuleContext) {
return {
TSAsExpression(node: TSAsExpression) {
TSAsExpression(node: Rule.Node) {
context.report({
node,
message: 'Avoid casting objects. Add necessary type checks instead.',
});
},
TSTypeAssertion(node: TSTypeAssertion) {
TSTypeAssertion(node: Rule.Node) {
context.report({
node,
message: 'Avoid casting objects. Add necessary type checks instead.',
Expand Down
Loading

0 comments on commit 423956e

Please sign in to comment.