Skip to content

Commit 5fa39b0

Browse files
Add custom ESLint rule to enforce block style guide
Co-authored-by: rita-gorokhod <60586879+rita-gorokhod@users.noreply.github.com>
1 parent 7ba287a commit 5fa39b0

6 files changed

Lines changed: 588 additions & 1 deletion

File tree

.eslintrc.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"root": true,
33
"ignorePatterns": ["**/*", "deploy/**/*"],
4-
"plugins": ["@nx"],
4+
"plugins": ["@nx", "@openops/block-style"],
55
"overrides": [
66
{
77
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.json"],
@@ -51,6 +51,12 @@
5151
"jest": true
5252
},
5353
"rules": {}
54+
},
55+
{
56+
"files": ["packages/blocks/**/*.ts"],
57+
"rules": {
58+
"@openops/block-style/block-style-guide": "error"
59+
}
5460
}
5561
],
5662
"settings": {
@@ -65,5 +71,8 @@
6571
"project": ["tsconfig.json", "package/tsconfig.json"]
6672
}
6773
}
74+
},
75+
"plugins": {
76+
"@openops/block-style": "./tools/eslint-rules"
6877
}
6978
}

tools/eslint-rules/README.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# OpenOps Block Style Guide ESLint Plugin
2+
3+
Custom ESLint plugin to enforce style guide rules for `displayName` and `description` fields in OpenOps blocks.
4+
5+
## Rules
6+
7+
### `block-style-guide`
8+
9+
Enforces the following style guide rules for block definitions:
10+
11+
#### 1. displayName - Title Case
12+
- Must be in Title Case (first letter of each word capitalized)
13+
- Exception: small words like "a", "and", "the", "of" can be lowercase (unless first word)
14+
- Acronyms (API, SQL, UUID, HTTP, etc.) should remain uppercase
15+
16+
**Examples:**
17+
```typescript
18+
// ✓ Good
19+
displayName: 'Get Recommendations'
20+
displayName: 'Execute SQL Statement'
21+
displayName: 'Send HTTP Request'
22+
displayName: 'Order By'
23+
24+
// ✗ Bad
25+
displayName: 'get recommendations'
26+
displayName: 'Execute sql statement'
27+
displayName: 'send Http request'
28+
```
29+
30+
#### 2. description - Sentence Case
31+
- Must start with a capital letter
32+
- Should NOT end with a period (unless multiple sentences)
33+
- Use imperative form for actions (Get, Create, Send) not third-person (Gets, Creates, Sends)
34+
35+
**Examples:**
36+
```typescript
37+
// ✓ Good
38+
description: 'Get cost recommendations from the provider'
39+
description: 'Send an email using SMTP. Supports attachments and HTML content.'
40+
41+
// ✗ Bad
42+
description: 'get cost recommendations'
43+
description: 'Get cost recommendations from the provider.' // No period for single sentence
44+
description: 'Gets cost recommendations' // Use imperative, not third-person
45+
```
46+
47+
#### 3. displayName and description Must Be Distinct
48+
- displayName and description should not be the same or too similar (>80% similarity)
49+
50+
**Examples:**
51+
```typescript
52+
// ✓ Good
53+
displayName: 'Get Recommendations'
54+
description: 'Fetch cost optimization suggestions from the provider'
55+
56+
// ✗ Bad
57+
displayName: 'Get Recommendations'
58+
description: 'Get recommendations' // Too similar
59+
```
60+
61+
#### 4. description Must Not Be Empty
62+
- Every description field must have meaningful content
63+
64+
**Examples:**
65+
```typescript
66+
// ✓ Good
67+
description: 'Create a new item in the database'
68+
69+
// ✗ Bad
70+
description: ''
71+
description: ' '
72+
```
73+
74+
#### 5. Action displayNames Should Use Verbs
75+
- For actions, displayName should start with an action verb
76+
- Common verbs: Get, Create, Update, Delete, Send, Execute, Run, Add, Remove, etc.
77+
78+
**Examples:**
79+
```typescript
80+
// ✓ Good (in createAction context)
81+
displayName: 'Get User Profile'
82+
displayName: 'Create Database Entry'
83+
displayName: 'Send Notification'
84+
85+
// ✗ Bad (in createAction context)
86+
displayName: 'User Profile' // Missing verb
87+
displayName: 'Database Entry Creation' // Not verb-first
88+
```
89+
90+
## Installation
91+
92+
The plugin is already integrated into the OpenOps monorepo. To enable it:
93+
94+
1. The plugin is located in `tools/eslint-rules/`
95+
2. It's configured in `.eslintrc.json` for the blocks package
96+
97+
## Usage
98+
99+
The rule runs automatically when you run ESLint:
100+
101+
```bash
102+
# Lint all files
103+
npm run lint
104+
105+
# Lint blocks specifically
106+
npx nx lint blocks
107+
108+
# Auto-fix issues where possible
109+
npx nx lint blocks --fix
110+
```
111+
112+
## Configuration
113+
114+
The rule is enabled by default for the blocks package. You can configure it in `.eslintrc.json`:
115+
116+
```json
117+
{
118+
"rules": {
119+
"@openops/block-style/block-style-guide": "error"
120+
}
121+
}
122+
```
123+
124+
## Development
125+
126+
To test the rule:
127+
128+
```bash
129+
# From the tools/eslint-rules directory
130+
npm test
131+
132+
# Or test against actual block files
133+
npx eslint packages/blocks/*/src/**/*.ts
134+
```
135+
136+
## Error Messages
137+
138+
The rule provides specific error messages for each violation:
139+
140+
- `displayNameNotTitleCase`: displayName should be in Title Case
141+
- `descriptionNotSentenceCase`: description should start with a capital letter
142+
- `descriptionTrailingPeriod`: description should not end with a period (unless multiple sentences)
143+
- `descriptionEmpty`: description must not be empty
144+
- `displayNameDescriptionSimilar`: displayName and description are too similar
145+
- `actionDisplayNameNotVerb`: Action displayName should start with a verb
146+
147+
## Examples
148+
149+
### Before (Violations)
150+
```typescript
151+
createAction({
152+
displayName: 'get recommendations', // Not Title Case
153+
description: 'Gets recommendations.', // Third-person verb + trailing period
154+
// ...
155+
});
156+
```
157+
158+
### After (Compliant)
159+
```typescript
160+
createAction({
161+
displayName: 'Get Recommendations', // Title Case
162+
description: 'Get cost optimization recommendations from the provider', // Imperative, no period
163+
// ...
164+
});
165+
```

tools/eslint-rules/examples.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Test examples for the block-style-guide ESLint rule
3+
*
4+
* These examples demonstrate violations and correct usage
5+
*/
6+
7+
// ❌ VIOLATIONS - These will trigger ESLint errors
8+
9+
// Violation: displayName not in Title Case
10+
const badAction1 = {
11+
displayName: 'get recommendations', // Should be "Get Recommendations"
12+
description: 'Get cost optimization recommendations',
13+
};
14+
15+
// Violation: description with trailing period (single sentence)
16+
const badAction2 = {
17+
displayName: 'Get Recommendations',
18+
description: 'Get cost optimization recommendations.', // Remove period
19+
};
20+
21+
// Violation: description not starting with capital
22+
const badAction3 = {
23+
displayName: 'Send Email',
24+
description: 'send an email using SMTP', // Should start with capital
25+
};
26+
27+
// Violation: empty description
28+
const badAction4 = {
29+
displayName: 'Create User',
30+
description: '', // Must not be empty
31+
};
32+
33+
// Violation: displayName and description too similar
34+
const badAction5 = {
35+
displayName: 'Delete Record',
36+
description: 'Delete record', // Too similar to displayName
37+
};
38+
39+
// Violation: Action displayName not starting with verb
40+
const badAction6 = {
41+
displayName: 'User Profile', // Should be "Get User Profile" or similar
42+
description: 'Get the user profile information',
43+
};
44+
45+
// Violation: Using third-person verb
46+
const badAction7 = {
47+
displayName: 'Update Settings',
48+
description: 'Updates the application settings', // Should be "Update" not "Updates"
49+
};
50+
51+
// ✅ CORRECT USAGE - These pass the linter
52+
53+
const goodAction1 = {
54+
displayName: 'Get Recommendations',
55+
description: 'Get cost optimization recommendations from the provider',
56+
};
57+
58+
const goodAction2 = {
59+
displayName: 'Send HTTP Request',
60+
description: 'Send an HTTP request to a specified URL',
61+
};
62+
63+
const goodAction3 = {
64+
displayName: 'Execute SQL Statement',
65+
description: 'Run an SQL query in a Databricks workspace and retrieve results',
66+
};
67+
68+
const goodAction4 = {
69+
displayName: 'Create Database Entry',
70+
description: 'Create a new entry in the database with the provided data',
71+
};
72+
73+
const goodAction5 = {
74+
displayName: 'Update User Profile',
75+
description: 'Update the user profile with new information. Validates all fields before saving.', // Multiple sentences OK with period
76+
};
77+
78+
const goodTrigger1 = {
79+
displayName: 'New Email',
80+
description: 'Trigger when a new email arrives in the inbox',
81+
};
82+
83+
const goodProperty1 = {
84+
displayName: 'API Token',
85+
description: 'Authentication token for API access',
86+
};
87+
88+
const goodProperty2 = {
89+
displayName: 'Order By',
90+
description: 'Sort the results by the specified field',
91+
};
92+
93+
// Edge cases that should pass
94+
95+
const edgeCaseAcronyms = {
96+
displayName: 'Send HTTP Request via API', // Acronyms OK
97+
description: 'Send an HTTP request using the REST API endpoint',
98+
};
99+
100+
const edgeCaseParentheses = {
101+
displayName: 'Get Volume IOPS (GB)', // Parentheses OK
102+
description: 'Get the IOPS value for the volume in gigabytes',
103+
};
104+
105+
const edgeCaseSpecialChars = {
106+
displayName: 'Execute SQL Query',
107+
description: 'Execute an SQL query (not a SQL) in the database', // "an SQL" is correct
108+
};

tools/eslint-rules/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Custom ESLint plugin for OpenOps block style guide enforcement
3+
*/
4+
5+
module.exports = {
6+
rules: {
7+
'block-style-guide': require('./rules/block-style-guide'),
8+
},
9+
};

tools/eslint-rules/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@openops/eslint-plugin-block-style",
3+
"version": "1.0.0",
4+
"description": "ESLint plugin to enforce OpenOps block style guide",
5+
"main": "index.js",
6+
"keywords": ["eslint", "eslintplugin", "openops", "blocks"],
7+
"author": "OpenOps",
8+
"license": "MIT"
9+
}

0 commit comments

Comments
 (0)