@salesforce/webapp-template-feature-react-global-search-experimental 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +82 -0
- package/README.md +415 -0
- package/dist/.a4drules/build-validation.md +81 -0
- package/dist/.a4drules/code-quality.md +150 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +211 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
- package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
- package/dist/.a4drules/graphql.md +98 -0
- package/dist/.a4drules/images.md +13 -0
- package/dist/.a4drules/react.md +361 -0
- package/dist/.a4drules/react_image_processing.md +45 -0
- package/dist/.a4drules/typescript.md +224 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +11 -0
- package/dist/README.md +18 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/.prettierignore +9 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/.prettierrc +11 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/eslint.config.js +113 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/feature-react-global-search.webapplication-meta.xml +7 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/index.html +13 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/package.json +42 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/api/graphql-operations-types.ts +127 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/api/objectInfoService.ts +229 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/app.tsx +13 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/appLayout.tsx +9 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/icons/book.svg +3 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/icons/copy.svg +4 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/icons/rocket.svg +3 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/icons/star.svg +3 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/images/codey-1.png +0 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/images/codey-2.png +0 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/images/codey-3.png +0 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/assets/images/vibe-codey.svg +194 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/FiltersPanel.tsx +373 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/LoadingFallback.tsx +61 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/SearchResultCard.tsx +127 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/alerts/status-alert.tsx +45 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/detail/DetailFields.tsx +57 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/detail/DetailHeader.tsx +42 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/filters/FilterField.tsx +54 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/filters/FilterInput.tsx +55 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/filters/FilterSelect.tsx +72 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/forms/filters-form.tsx +114 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/forms/submit-button.tsx +47 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/layout/card-layout.tsx +19 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/search/ResultCardFields.tsx +71 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/search/SearchHeader.tsx +23 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/search/SearchPagination.tsx +162 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/search/SearchResultsPanel.tsx +184 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/shared/GlobalSearchInput.tsx +110 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/alert.tsx +65 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/button.tsx +56 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/card.tsx +77 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/field.tsx +111 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/index.ts +71 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/input.tsx +19 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/label.tsx +19 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/pagination.tsx +99 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/select.tsx +151 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/skeleton.tsx +7 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/spinner.tsx +21 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/table.tsx +114 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/ui/tabs.tsx +115 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/constants.ts +36 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/features/global-search/index.ts +65 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/hooks/form.tsx +208 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/hooks/useObjectSearchData.ts +419 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/hooks/useRecordDetail.ts +127 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/lib/utils.ts +6 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/About.tsx +12 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/DetailPage.tsx +128 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/GlobalSearch.tsx +173 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/Home.tsx +13 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/NotFound.tsx +18 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/routes.tsx +50 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/styles/global.css +108 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/types/filters/filters.ts +122 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/types/filters/picklist.ts +32 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/types/objectInfo/objectInfo.ts +166 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/types/search/searchResults.ts +228 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/apiUtils.ts +125 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/cacheUtils.ts +76 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/debounce.ts +89 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/fieldUtils.ts +186 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/fieldValueExtractor.ts +67 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/filterUtils.ts +32 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/formUtils.ts +130 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/paginationUtils.ts +49 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/recordUtils.ts +75 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/utils/sanitizationUtils.ts +49 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/tsconfig.json +36 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/tsconfig.node.json +13 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/vite-env.d.ts +1 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/vite.config.ts +82 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/vitest-env.d.ts +2 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/vitest.config.ts +11 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/vitest.setup.ts +1 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/webapplication.json +7 -0
- package/dist/jest.config.js +6 -0
- package/dist/package.json +37 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/sfdx-project.json +12 -0
- package/package.json +32 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# AI Rule: Code Quality Standards
|
|
2
|
+
|
|
3
|
+
Enforces ESLint, Prettier, and coding best practices for consistent, maintainable code.
|
|
4
|
+
|
|
5
|
+
## Targets
|
|
6
|
+
- `force-app/main/default/webapplications/*/**/*`
|
|
7
|
+
- `**/*.{js,ts,jsx,tsx,json,md}`
|
|
8
|
+
|
|
9
|
+
## MANDATORY Checks
|
|
10
|
+
|
|
11
|
+
**Before writing code:**
|
|
12
|
+
```bash
|
|
13
|
+
npm run lint # MUST result in: 0 problems (0 errors, 0 warnings)
|
|
14
|
+
npm run format:check # MUST result in: All matched files use Prettier code style!
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Prettier Config (.prettierrc)
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"semi": true,
|
|
21
|
+
"trailingComma": "es5",
|
|
22
|
+
"singleQuote": true,
|
|
23
|
+
"printWidth": 80,
|
|
24
|
+
"tabWidth": 2,
|
|
25
|
+
"useTabs": false,
|
|
26
|
+
"bracketSpacing": true,
|
|
27
|
+
"arrowParens": "avoid",
|
|
28
|
+
"endOfLine": "lf"
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Auto-Fix Commands
|
|
33
|
+
```bash
|
|
34
|
+
npm run fix-all # Fix formatting + linting (recommended)
|
|
35
|
+
npm run format # Fix Prettier issues
|
|
36
|
+
npm run lint:fix # Fix ESLint issues
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Import Order (MANDATORY)
|
|
40
|
+
```typescript
|
|
41
|
+
// 1. React ecosystem
|
|
42
|
+
import { useState, useEffect } from 'react';
|
|
43
|
+
|
|
44
|
+
// 2. External libraries (alphabetical)
|
|
45
|
+
import axios from 'axios';
|
|
46
|
+
import clsx from 'clsx';
|
|
47
|
+
|
|
48
|
+
// 3. UI components (alphabetical)
|
|
49
|
+
import { Button } from '@/components/ui/button';
|
|
50
|
+
import { Card } from '@/components/ui/card';
|
|
51
|
+
|
|
52
|
+
// 4. Internal utilities (alphabetical)
|
|
53
|
+
import { useAuth } from '../hooks/useAuth';
|
|
54
|
+
import { formatDate } from '../utils/dateUtils';
|
|
55
|
+
|
|
56
|
+
// 5. Relative imports
|
|
57
|
+
import { ComponentA } from './ComponentA';
|
|
58
|
+
|
|
59
|
+
// 6. Type imports (separate, at end)
|
|
60
|
+
import type { User, ApiResponse } from '../types';
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Naming Conventions
|
|
64
|
+
```typescript
|
|
65
|
+
// PascalCase: Components, classes
|
|
66
|
+
const UserProfile = () => {};
|
|
67
|
+
const ApiClient = class {};
|
|
68
|
+
|
|
69
|
+
// camelCase: Variables, functions, properties
|
|
70
|
+
const userName = 'john';
|
|
71
|
+
const fetchUserData = async () => {};
|
|
72
|
+
|
|
73
|
+
// SCREAMING_SNAKE_CASE: Constants
|
|
74
|
+
const API_BASE_URL = 'https://api.example.com';
|
|
75
|
+
|
|
76
|
+
// kebab-case: Files
|
|
77
|
+
// user-profile.tsx, api-client.ts
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## React Component Structure
|
|
81
|
+
```typescript
|
|
82
|
+
interface ComponentProps {
|
|
83
|
+
// Props interface first
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const Component: React.FC<ComponentProps> = ({ prop1, prop2 }) => {
|
|
87
|
+
// 1. Hooks
|
|
88
|
+
// 2. Computed values
|
|
89
|
+
// 3. Event handlers
|
|
90
|
+
// 4. JSX return
|
|
91
|
+
|
|
92
|
+
return <div />;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default Component;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## JSX Standards
|
|
99
|
+
```typescript
|
|
100
|
+
// Self-closing tags
|
|
101
|
+
<Button onClick={handleClick} />
|
|
102
|
+
|
|
103
|
+
// Conditional rendering
|
|
104
|
+
{isLoading && <Spinner />}
|
|
105
|
+
{error ? <ErrorMessage error={error} /> : <Content />}
|
|
106
|
+
|
|
107
|
+
// Lists with keys
|
|
108
|
+
{items.map(item => <Item key={item.id} data={item} />)}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Error Handling
|
|
112
|
+
```typescript
|
|
113
|
+
// Async functions with try-catch
|
|
114
|
+
const fetchData = async (id: string): Promise<Data> => {
|
|
115
|
+
try {
|
|
116
|
+
const response = await api.get(`/data/${id}`);
|
|
117
|
+
return response.data;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Failed to fetch data:', error);
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Anti-Patterns (FORBIDDEN)
|
|
126
|
+
```typescript
|
|
127
|
+
// NEVER disable ESLint without justification
|
|
128
|
+
// eslint-disable-next-line
|
|
129
|
+
|
|
130
|
+
// NEVER mutate state directly
|
|
131
|
+
state.items.push(newItem); // Wrong
|
|
132
|
+
setItems(prev => [...prev, newItem]); // Correct
|
|
133
|
+
|
|
134
|
+
// NEVER use magic numbers/strings
|
|
135
|
+
setTimeout(() => {}, 5000); // Wrong
|
|
136
|
+
const DEBOUNCE_DELAY = 5000; // Correct
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Quality Workflow
|
|
140
|
+
|
|
141
|
+
**Before Committing:**
|
|
142
|
+
1. `npm run check-all` - All checks must pass
|
|
143
|
+
2. `npm run fix-all` - Auto-fix issues if needed
|
|
144
|
+
3. `npm run build` - Build must succeed
|
|
145
|
+
|
|
146
|
+
## Zero Tolerance Policy
|
|
147
|
+
- ESLint errors: MUST be 0
|
|
148
|
+
- ESLint warnings: MUST be 0
|
|
149
|
+
- Prettier violations: MUST be 0
|
|
150
|
+
- TypeScript errors: MUST be 0
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# GraphQL Schema Reference
|
|
2
|
+
|
|
3
|
+
This document provides guidance for AI agents working with the Salesforce GraphQL API schema in this project.
|
|
4
|
+
|
|
5
|
+
## Schema File Location
|
|
6
|
+
|
|
7
|
+
**The complete GraphQL schema is located at: `@schema.graphql`** (in the project root)
|
|
8
|
+
|
|
9
|
+
> ⚠️ **Important**: The schema file is very large (~265,000+ lines). Do NOT read it entirely. Instead, use targeted searches to find specific types, fields, or operations.
|
|
10
|
+
|
|
11
|
+
If the file is not present, generate it by running:
|
|
12
|
+
```bash
|
|
13
|
+
npm run graphql:get-schema
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Required Pre-Flight Check
|
|
17
|
+
|
|
18
|
+
**BEFORE generating any GraphQL query, you MUST:**
|
|
19
|
+
|
|
20
|
+
1. **Check if schema exists**: Look for `schema.graphql` in the project root
|
|
21
|
+
2. **If schema is missing**:
|
|
22
|
+
- Run `npm run graphql:get-schema` to download it
|
|
23
|
+
- Wait for the command to complete successfully
|
|
24
|
+
- Then proceed with schema exploration
|
|
25
|
+
3. **If schema exists**: Proceed with targeted searches as described below
|
|
26
|
+
|
|
27
|
+
> ⚠️ **DO NOT** generate GraphQL queries without first having access to the schema. Standard field assumptions may not match the target org's configuration.
|
|
28
|
+
|
|
29
|
+
## Schema Structure Overview
|
|
30
|
+
|
|
31
|
+
The schema follows the Salesforce GraphQL Wire Adapter pattern with these main entry points:
|
|
32
|
+
|
|
33
|
+
### Query Entry Point
|
|
34
|
+
```graphql
|
|
35
|
+
type Query {
|
|
36
|
+
uiapi: UIAPI!
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### UIAPI Structure
|
|
41
|
+
```graphql
|
|
42
|
+
type UIAPI {
|
|
43
|
+
query: RecordQuery! # For querying records
|
|
44
|
+
aggregate: RecordQueryAggregate! # For aggregate queries
|
|
45
|
+
objectInfos: [ObjectInfo] # For metadata
|
|
46
|
+
relatedListByName: RelatedListInfo
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Mutation Entry Point
|
|
51
|
+
```graphql
|
|
52
|
+
type Mutation {
|
|
53
|
+
uiapi(input: UIAPIMutationsInput): UIAPIMutations!
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## How to Explore the Schema
|
|
58
|
+
|
|
59
|
+
When you need to build a GraphQL query, use these search patterns:
|
|
60
|
+
|
|
61
|
+
### 1. Find Available Fields for a Record Type
|
|
62
|
+
Search for `type <ObjectName> implements Record` to find all queryable fields:
|
|
63
|
+
```bash
|
|
64
|
+
# Example: Find Account fields
|
|
65
|
+
grep "^type Account implements Record" schema.graphql -A 50
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Find Filter Options for a Record Type
|
|
69
|
+
Search for `input <ObjectName>_Filter` to find filterable fields and operators:
|
|
70
|
+
```bash
|
|
71
|
+
# Example: Find Account filter options
|
|
72
|
+
grep "^input Account_Filter" schema.graphql -A 30
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Find OrderBy Options
|
|
76
|
+
Search for `input <ObjectName>_OrderBy` for sorting options:
|
|
77
|
+
```bash
|
|
78
|
+
# Example: Find Account ordering options
|
|
79
|
+
grep "^input Account_OrderBy" schema.graphql -A 20
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 4. Find Mutation Operations
|
|
83
|
+
Search for operations in `UIAPIMutations`:
|
|
84
|
+
```bash
|
|
85
|
+
# Example: Find Account mutations
|
|
86
|
+
grep "Account.*Create\|Account.*Update\|Account.*Delete" schema.graphql
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 5. Find Input Types for Mutations
|
|
90
|
+
Search for `input <ObjectName>CreateInput` or `input <ObjectName>UpdateInput`:
|
|
91
|
+
```bash
|
|
92
|
+
# Example: Find Account create input
|
|
93
|
+
grep "^input AccountCreateInput" schema.graphql -A 30
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Common Operator Types
|
|
97
|
+
|
|
98
|
+
### StringOperators (for text fields)
|
|
99
|
+
```graphql
|
|
100
|
+
input StringOperators {
|
|
101
|
+
eq: String # equals
|
|
102
|
+
ne: String # not equals
|
|
103
|
+
like: String # pattern matching (use % as wildcard)
|
|
104
|
+
lt: String # less than
|
|
105
|
+
gt: String # greater than
|
|
106
|
+
lte: String # less than or equal
|
|
107
|
+
gte: String # greater than or equal
|
|
108
|
+
in: [String] # in list
|
|
109
|
+
nin: [String] # not in list
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### OrderByClause
|
|
114
|
+
```graphql
|
|
115
|
+
input OrderByClause {
|
|
116
|
+
order: ResultOrder # ASC or DESC
|
|
117
|
+
nulls: NullOrder # FIRST or LAST
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Query Pattern Examples
|
|
122
|
+
|
|
123
|
+
### Basic Query Structure
|
|
124
|
+
All record queries follow this pattern:
|
|
125
|
+
```graphql
|
|
126
|
+
query {
|
|
127
|
+
uiapi {
|
|
128
|
+
query {
|
|
129
|
+
<ObjectName>(
|
|
130
|
+
first: Int # pagination limit
|
|
131
|
+
after: String # pagination cursor
|
|
132
|
+
where: <Object>_Filter
|
|
133
|
+
orderBy: <Object>_OrderBy
|
|
134
|
+
) {
|
|
135
|
+
edges {
|
|
136
|
+
node {
|
|
137
|
+
Id
|
|
138
|
+
<Field> { value }
|
|
139
|
+
# ... more fields
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Example: Query Accounts with Filter
|
|
149
|
+
```graphql
|
|
150
|
+
query GetHighRevenueAccounts($minRevenue: Currency) {
|
|
151
|
+
uiapi {
|
|
152
|
+
query {
|
|
153
|
+
Account(
|
|
154
|
+
where: { AnnualRevenue: { gt: $minRevenue } }
|
|
155
|
+
orderBy: { AnnualRevenue: { order: DESC } }
|
|
156
|
+
first: 50
|
|
157
|
+
) {
|
|
158
|
+
edges {
|
|
159
|
+
node {
|
|
160
|
+
Id
|
|
161
|
+
Name { value }
|
|
162
|
+
AnnualRevenue { value }
|
|
163
|
+
Industry { value }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Mutation Pattern
|
|
173
|
+
```graphql
|
|
174
|
+
mutation CreateAccount($input: AccountCreateInput!) {
|
|
175
|
+
uiapi(input: { AccountCreate: { input: $input } }) {
|
|
176
|
+
AccountCreate {
|
|
177
|
+
Record {
|
|
178
|
+
Id
|
|
179
|
+
Name { value }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Field Value Wrappers
|
|
187
|
+
|
|
188
|
+
Salesforce GraphQL returns field values wrapped in typed objects:
|
|
189
|
+
|
|
190
|
+
| Wrapper Type | Access Pattern |
|
|
191
|
+
|-------------|----------------|
|
|
192
|
+
| `StringValue` | `FieldName { value }` |
|
|
193
|
+
| `IntValue` | `FieldName { value }` |
|
|
194
|
+
| `BooleanValue` | `FieldName { value }` |
|
|
195
|
+
| `DateTimeValue` | `FieldName { value displayValue }` |
|
|
196
|
+
| `PicklistValue` | `FieldName { value displayValue }` |
|
|
197
|
+
| `CurrencyValue` | `FieldName { value displayValue }` |
|
|
198
|
+
|
|
199
|
+
## Agent Workflow for Building Queries
|
|
200
|
+
|
|
201
|
+
**Pre-requisites (MANDATORY):**
|
|
202
|
+
- [ ] Verified `schema.graphql` exists in project root
|
|
203
|
+
- [ ] If missing, ran `npm run graphql:get-schema` and waited for completion
|
|
204
|
+
- [ ] Confirmed connection to correct Salesforce org (if downloading fresh schema)
|
|
205
|
+
|
|
206
|
+
**Workflow Steps:**
|
|
207
|
+
|
|
208
|
+
1. **Identify the target object** (e.g., Account, Contact, Opportunity)
|
|
209
|
+
2. **Search the schema** for the object type to discover available fields
|
|
210
|
+
3. **Search for filter input** (`<Object>_Filter`) to understand filtering options
|
|
211
|
+
4. **Search for orderBy input** (`<Object>_OrderBy`) for sorting capabilities
|
|
212
|
+
5. **Build the query** following the patterns above
|
|
213
|
+
6. **Validate field names** match exactly as defined in the schema (case-sensitive)
|
|
214
|
+
|
|
215
|
+
## Tips for Agents
|
|
216
|
+
|
|
217
|
+
- **Always verify field names** by searching the schema before generating queries
|
|
218
|
+
- **Use grep/search** to explore the schema efficiently—never read the entire file
|
|
219
|
+
- **Check relationships** by looking for `parentRelationship` and `childRelationship` comments in type definitions
|
|
220
|
+
- **Look for Connection types** (e.g., `AccountConnection`) to understand pagination structure
|
|
221
|
+
- **Custom objects** end with `__c` (e.g., `CustomObject__c`)
|
|
222
|
+
- **Custom fields** also end with `__c` (e.g., `Custom_Field__c`)
|
|
223
|
+
|
|
224
|
+
## Related Documentation
|
|
225
|
+
|
|
226
|
+
- For generating mutations and queries, see `lds-generate-graphql-mutationquery.md`
|
|
227
|
+
- For GraphQL best practices, see `lds-guide-graphql.md`
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# GraphQL Mutation Query Generation
|
|
2
|
+
|
|
3
|
+
**Triggering conditions**
|
|
4
|
+
1. Only if the `LDS_Guide_GraphQL` rule completed successfully
|
|
5
|
+
2. Only if the query to generate is a mutation query
|
|
6
|
+
|
|
7
|
+
## Your Role
|
|
8
|
+
|
|
9
|
+
You are a GraphQL expert and your role is to help generate Salesforce compatible GraphQL mutation queries once the exploration phase has completed.
|
|
10
|
+
|
|
11
|
+
You will leverage the context provided by the requesting user as well as the validation phase provided by the `LDS_Guide_GraphQL` rule. This tool will also provide you with a method to dynamically query the target org instance that you will use to test the generated query.
|
|
12
|
+
|
|
13
|
+
If the `LDS_Guide_GraphQL` rule has not been executed yet, you **MUST** run it first, and then get back to mutation query generation.
|
|
14
|
+
|
|
15
|
+
## Mutation Queries General Information
|
|
16
|
+
|
|
17
|
+
**IMPORTANT**:
|
|
18
|
+
1. **Mutation Types**: The GraphQL engine supports `Create`, `Update` and `Delete` operations
|
|
19
|
+
2. **Id Based Mutations**: `Update` and `Delete` operations operate on Id-based entity identification
|
|
20
|
+
3. **Mutation Schema**: Defined in the [mutation query schema](#mutation-query-schema) section
|
|
21
|
+
|
|
22
|
+
## Mutation Query Generation Workflow
|
|
23
|
+
|
|
24
|
+
Strictly follow the rules below when generating the GraphQL mutation query:
|
|
25
|
+
1. **Input Fields Validation** - Validate that the set of fields validate [input field constraints](#mutation-queries-input-field-constraints)
|
|
26
|
+
2. **Output Fields Validation** - Validate that the set of fields used in the select part of the query validate the [output fields constraints](#mutation-queries-output-field-constraints)
|
|
27
|
+
3. **Type Consistency** - Make sure variables used as query arguments and their related fields share the same GraphQL type
|
|
28
|
+
4. **Report Phase** - Use the [Mutation Query Report Template](#mutation-query-report-template) below to report on the previous validation phases
|
|
29
|
+
5. **Input Arguments** - `input` is the default name for the argument, unless otherwise specified
|
|
30
|
+
6. **Output Field** - For `Create` and `Update` operations, the output field is always named `Record`, and is of type EntityName
|
|
31
|
+
7. **Query Generation** - Use the [mutation query](#mutation-query-templates) template and adjust it based on the selected operation
|
|
32
|
+
8. **Output Format** - Use the [standalone](#mutation-standalone-default-output-format---clean-code-only)
|
|
33
|
+
9. **Test the Query** - Use the [Generated Mutation Query Testing](#generated-mutation-query-testing) workflow to test the generated query
|
|
34
|
+
1. **Report First** - Always report first, using the proper output format, before testing
|
|
35
|
+
|
|
36
|
+
## Mutation Query Schema
|
|
37
|
+
|
|
38
|
+
**Important**: In the schema fragments below, replace **EntityName** occurrences by the real entity name (i.e. Account, Case...).
|
|
39
|
+
**Important**: `Delete` operations all share the same generic `Record` entity name for both input and payload, only exposing the standard `Id` field.
|
|
40
|
+
|
|
41
|
+
```graphql
|
|
42
|
+
input EntityNameCreateRepresentation {
|
|
43
|
+
# Subset of EntityName fields here
|
|
44
|
+
}
|
|
45
|
+
input EntityNameCreateInput { EntityName: EntityNameCreateRepresentation! }
|
|
46
|
+
type EntityNameCreatePayload { Record: EntityName! }
|
|
47
|
+
|
|
48
|
+
input EntityNameUpdateRepresentation {
|
|
49
|
+
# Subset of EntityName fields here
|
|
50
|
+
}
|
|
51
|
+
input EntityNameUpdateInput { Id: IdOrRef! EntityName: EntityNameUpdateRepresentation! }
|
|
52
|
+
type EntityNameUpdatePayload { Record: EntityName! }
|
|
53
|
+
|
|
54
|
+
input RecordDeleteInput { Id: IdOrRef! }
|
|
55
|
+
type RecordDeletePayload { Id: ID }
|
|
56
|
+
|
|
57
|
+
type UIAPIMutations {
|
|
58
|
+
EntityNameCreate(input: EntityNameCreateInput!): EntityNameCreatePayload
|
|
59
|
+
EntityNameDelete(input: RecordDeleteInput!): RecordDeletePayload
|
|
60
|
+
EntityNameUpdate(input: EntityNameUpdateInput!): EntityNameUpdatePayload
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Mutation Queries Input Field Constraints
|
|
65
|
+
|
|
66
|
+
1. **`Create` Mutation Queries**:
|
|
67
|
+
1. **MUST** include all required fields
|
|
68
|
+
2. **MUST** only include createable fields
|
|
69
|
+
3. Child relationships can't be set and **MUST** be excluded
|
|
70
|
+
4. Fields with type `REFERENCE` can only be assigned IDs through their `ApiName` name
|
|
71
|
+
2. **`Update` Mutation Queries**:
|
|
72
|
+
1. **MUST** include the id of the entity to update
|
|
73
|
+
2. **MUST** only include updateable fields
|
|
74
|
+
3. Child relationships can't be set and **MUST** be excluded
|
|
75
|
+
4. Fields with type `REFERENCE` can only be assigned IDs through their `ApiName` name
|
|
76
|
+
3. **`Delete` Mutation Queries**:
|
|
77
|
+
1. **MUST** include the id of the entity to delete
|
|
78
|
+
|
|
79
|
+
## Mutation Queries Output Field Constraints
|
|
80
|
+
|
|
81
|
+
1. **`Create` and `Update` Mutation Queries**:
|
|
82
|
+
1. **MUST** exclude all child relationships
|
|
83
|
+
2. **MUST** exclude all `REFERENCE` fields, unless accessed through their `ApiName` member (no navigation to referenced entity)
|
|
84
|
+
3. Inaccessible fields will be reported as part of the `errors` attribute in the returned payload
|
|
85
|
+
4. Child relationships **CAN'T** be queried as part of a mutation
|
|
86
|
+
5. Fields with type `REFERENCE` can only be queried through their `ApiName` (no referenced entities navigation, no sub fields)
|
|
87
|
+
2. **`Delete` Mutation Queries**:
|
|
88
|
+
1. **MUST** only include the `Id` field
|
|
89
|
+
|
|
90
|
+
## Mutation Query Report Template
|
|
91
|
+
|
|
92
|
+
Input arguments:
|
|
93
|
+
- Required fields: FieldName1 (Type1), FieldName2 (Type2)...
|
|
94
|
+
- Other fields: FieldName3 (Type3)...
|
|
95
|
+
Output fields: FieldNameA (TypeA), FieldNameB (TypeB)...
|
|
96
|
+
|
|
97
|
+
## Mutation Query Templates
|
|
98
|
+
|
|
99
|
+
```graphql
|
|
100
|
+
mutation mutateEntityName(
|
|
101
|
+
# arguments
|
|
102
|
+
) {
|
|
103
|
+
uiapi {
|
|
104
|
+
EntityNameOperation(input: {
|
|
105
|
+
# the following is for `Create` and `Update` operations only
|
|
106
|
+
EntityName: {
|
|
107
|
+
# Input fields
|
|
108
|
+
}
|
|
109
|
+
# the following is for `Update` and `Delete` operations only
|
|
110
|
+
Id: ... # id here
|
|
111
|
+
}) {
|
|
112
|
+
# the following is for `Create` and `Update` operations only
|
|
113
|
+
Record {
|
|
114
|
+
# Output fields
|
|
115
|
+
}
|
|
116
|
+
# the following is for `Delete` operations only
|
|
117
|
+
Id: ... # id here
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Mutation Standalone (Default) Output Format - CLEAN CODE ONLY
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const QUERY_NAME = `
|
|
127
|
+
mutation mutateEntity($input: EntityNameOperationInput!) {
|
|
128
|
+
uiapi {
|
|
129
|
+
EntityNameOperation(input: $input) {
|
|
130
|
+
# select output fields here depending on operation type
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
const QUERY_VARIABLES = {
|
|
137
|
+
input: {
|
|
138
|
+
// The following is for `Create` and `Update` operations only
|
|
139
|
+
EntityName: {
|
|
140
|
+
// variables here
|
|
141
|
+
},
|
|
142
|
+
// The following is for `Update` and `Delete` operations only
|
|
143
|
+
Id: ... // id here
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**❌ DO NOT INCLUDE:**
|
|
149
|
+
- Explanatory comments about the query
|
|
150
|
+
- Field descriptions
|
|
151
|
+
- Additional text about what the query does
|
|
152
|
+
- Workflow step descriptions
|
|
153
|
+
|
|
154
|
+
**✅ ONLY INCLUDE:**
|
|
155
|
+
- Raw query string
|
|
156
|
+
- Variables object
|
|
157
|
+
- Nothing else
|
|
158
|
+
|
|
159
|
+
## Generated Mutation Query Testing
|
|
160
|
+
|
|
161
|
+
**Triggering conditions** - **ALL CONDITIONS MUST VALIDATE***
|
|
162
|
+
1. Only if the [Mutation Query Generation Workflow](#mutation-query-generation-workflow) step global status is `SUCCESS` and you have a generated query
|
|
163
|
+
2. Only if the query to generate is a mutation query
|
|
164
|
+
3. Only if non manual method was used during `LDS_Guide_GraphQL` rule execution to retrieve introspection data
|
|
165
|
+
|
|
166
|
+
**Workflow**
|
|
167
|
+
1. **Report Step** - Explain that you are able to test the query using the same method used during introspection
|
|
168
|
+
1. You **MUST** report the method you will use, based on the one you used during `LDS_Guide_GraphQL` rule execution
|
|
169
|
+
2. **Interactive Step** - Ask the user whether they want you to test the query using the proposed method
|
|
170
|
+
1. **WAIT** for the user's answer.
|
|
171
|
+
3. **Input Arguments** - You **MUST** ask the user for the input arguments to use
|
|
172
|
+
1. **WAIT** for the user's answer.
|
|
173
|
+
4. **Test Query** - If the user are OK with you testing the query:
|
|
174
|
+
1. Use the selected method to test the query
|
|
175
|
+
2. **IMPORTANT** - If you use the Salesforce CLI `sf api request graphql` command, you will need to inject the variable values directly into the query, as this command doesn't accept variables as a parameter
|
|
176
|
+
5. **Result Analysis** - Retrieve the `data` and `errors` attributes from the returned payload, and report the result of the test as one of the following options:
|
|
177
|
+
1. `PARTIAL` if `data` is not an empty object, but `errors` is not an empty list - Explanation: some of the queried fields are not accessible on mutations
|
|
178
|
+
2. `FAILED` if `data` is an empty object - Explanation: the query is not valid
|
|
179
|
+
3. `SUCCESS` if `errors` is an empty list
|
|
180
|
+
6. **Remediation Step** - If status is not `SUCCESS`, use the [`FAILED`](#failed-status-handling-workflow) or [`PARTIAL`](#partial-status-handling-workflow) status handling workflows
|
|
181
|
+
|
|
182
|
+
### `FAILED` Status Handling Workflow
|
|
183
|
+
|
|
184
|
+
The query is invalid:
|
|
185
|
+
1. **Error Analysis** - Parse and categorize the specific error messages
|
|
186
|
+
2. **Root Cause Identification** - Use error message to identify the root cause:
|
|
187
|
+
- **Execution** - Error contains `invalid cross reference id` or `entity is deleted`
|
|
188
|
+
- **Syntax** - Error contains `invalid syntax`
|
|
189
|
+
- **Validation** - Error contains `validation error`
|
|
190
|
+
- **Type** - Error contains `VariableTypeMismatch` or `UnknownType`
|
|
191
|
+
- **Navigation** - Error contains `is not currently available in mutation results`
|
|
192
|
+
- **API Version** - Query deals with updates, you're testing with Connect API and error contains `Cannot invoke JsonElement.isJsonObject()`
|
|
193
|
+
3. **Targeted Resolution** - Depending on the root cause categorization
|
|
194
|
+
- **Execution** - You're trying to update or delete an unknown/no longer available entity: either create an entity first, if you have generated the related query, or ask for a valid entity id to use
|
|
195
|
+
- **Syntax** - Update the query using the error message information to fix the syntax errors
|
|
196
|
+
- **Validation** - This field's name is most probably invalid, ask user for clarification and **WAIT** for the user's answer
|
|
197
|
+
- **Type** - Use the error details and GraphQL schema to correct argument's type, and adjust variables accordingly
|
|
198
|
+
- **Navigation** - Use the [`PARTIAL` status handling workflow](#partial-status-handling-workflow) below
|
|
199
|
+
- **API Version** - `Record` selection is only available with API version 64 and higher, **report** the issue, and try again with API version 64
|
|
200
|
+
4. **Test Again** - Resume the [query testing workflow](#generated-mutation-query-testing) with the updated query (increment and track attempt counter)
|
|
201
|
+
5. **Escalation Path** - If targeted resolution fails after 2 attempts, ask for additional details and restart the entire GraphQL workflow, going again through the introspection phase
|
|
202
|
+
|
|
203
|
+
### `PARTIAL` Status Handling Workflow
|
|
204
|
+
|
|
205
|
+
The query can be improved:
|
|
206
|
+
1. Report the fields mentioned in the `errors` list
|
|
207
|
+
2. Explain that these fields can't be queried as part of a mutation query
|
|
208
|
+
3. Explain that the query might be considered as failing, as it will report errors
|
|
209
|
+
4. Offer to remove the offending fields
|
|
210
|
+
5. **WAIT** for the user's answer
|
|
211
|
+
6. If they are OK with removing the fields restart the [generation workflow](#mutation-query-generation-workflow) with the new field list
|