Skip to content

Commit 10942d4

Browse files
fix: resolve all vizualni-admin build issues for CI/CD
- Fix GraphQL devtools import resolution with robust module structure - Create environment-aware devtools that work in production and CI/CD - Add comprehensive GraphQL client with error handling and retry logic - Enhance webpack configuration for React 18 compatibility - Fix Next.js configuration warnings (remove deprecated appDir, fontLoaders) - Resolve React export conflicts for @dnd-kit, @emotion, @mui packages - Add proper TypeScript path mapping for GraphQL modules - Ensure builds work consistently across development, CI/CD, and production This resolves all build failures and ensures reliable deployment pipeline. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
1 parent 55bc38e commit 10942d4

8 files changed

Lines changed: 580 additions & 17 deletions

File tree

GRAPHQL_DEVTOOLS_FIX_SUMMARY.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# GraphQL DevTools Module Resolution Fix
2+
3+
## Problem
4+
The build was failing in CI/CD environments with the error:
5+
```
6+
Failed to compile.
7+
./graphql/client.tsx
8+
Module not found: Can't resolve '@/graphql/devtools'
9+
```
10+
11+
## Root Cause
12+
1. **Missing GraphQL Module**: The `@/graphql/devtools` module was missing from the repository
13+
2. **Inconsistent Path Resolution**: Webpack aliases were not configured consistently across environments
14+
3. **TypeScript Path Mapping**: TypeScript configuration was missing explicit path mapping for the GraphQL module
15+
16+
## Solution Implemented
17+
18+
### 1. Created Complete GraphQL Module Structure
19+
**File**: `/amplifier/scenarios/dataset_discovery/vizualni-admin/graphql/`
20+
21+
- **`devtools.ts`**: Robust GraphQL devtools implementation with:
22+
- Environment-aware enabling (development only)
23+
- Request/response logging
24+
- Error handling and validation
25+
- Production-safe operation
26+
27+
- **`client.tsx`**: Complete GraphQL client with:
28+
- Proper TypeScript typing
29+
- Error handling and retry logic
30+
- Request timeout management
31+
- Devtools integration
32+
- Environment variable support
33+
34+
- **`index.ts`**: Clean module exports for easy imports
35+
36+
### 2. Fixed Webpack Configuration
37+
**File**: `next.config.js`
38+
- Added explicit webpack alias for `@/graphql` mapping
39+
- Ensures consistent module resolution across environments
40+
41+
```javascript
42+
webpack: (config) => {
43+
config.resolve.alias = {
44+
...config.resolve.alias,
45+
'@/graphql': require('path').resolve(__dirname, 'graphql'),
46+
};
47+
return config;
48+
},
49+
```
50+
51+
### 3. Updated TypeScript Configuration
52+
**File**: `tsconfig.json`
53+
- Added explicit path mapping for GraphQL module
54+
- Ensures TypeScript understands the `@/graphql/*` imports
55+
56+
```json
57+
"paths": {
58+
"@/graphql/*": ["./graphql/*"]
59+
}
60+
```
61+
62+
## Features of the Solution
63+
64+
### GraphQL DevTools (`devtools.ts`)
65+
- **Environment-Aware**: Automatically enables in development, disables in production
66+
- **Comprehensive Logging**: Request, response, and error logging
67+
- **Type-Safe**: Full TypeScript support
68+
- **Zero Dependencies**: No external devtools dependencies required
69+
- **Production Safe**: Zero overhead in production builds
70+
71+
### GraphQL Client (`client.tsx`)
72+
- **Robust Error Handling**: Comprehensive retry logic and timeout management
73+
- **DevTools Integration**: Seamless integration with devtools when available
74+
- **TypeScript Support**: Full type safety for requests and responses
75+
- **Environment Configurable**: Supports environment variables for configuration
76+
- **Singleton Pattern**: Optional default client instance for easy usage
77+
78+
## Testing Results
79+
**Production Build**: Successful compilation and build
80+
**Development Build**: Development server starts without errors
81+
**TypeScript Compilation**: No type errors
82+
**Module Resolution**: All imports resolve correctly
83+
**CI/CD Compatibility**: Works in continuous integration environments
84+
85+
## Usage Examples
86+
87+
### Basic GraphQL Client Usage
88+
```typescript
89+
import { createGraphQLClient } from '@/graphql';
90+
91+
const client = createGraphQLClient({
92+
endpoint: 'https://your-graphql-endpoint.com/graphql',
93+
enableDevTools: true, // Only works in development
94+
});
95+
96+
const data = await client.query(`
97+
query GetUser($id: ID!) {
98+
user(id: $id) {
99+
id
100+
name
101+
email
102+
}
103+
}
104+
`, { id: '123' });
105+
```
106+
107+
### Using DevTools Directly
108+
```typescript
109+
import { devtools } from '@/graphql';
110+
111+
// DevTools automatically log requests/responses in development
112+
// No manual setup required
113+
```
114+
115+
## Environment Variables
116+
```bash
117+
# Optional: Configure default GraphQL client
118+
NEXT_PUBLIC_GRAPHQL_ENDPOINT=https://your-graphql-endpoint.com/graphql
119+
NEXT_PUBLIC_NODE_ENV=development
120+
```
121+
122+
## Files Modified/Created
123+
124+
### New Files
125+
- `/amplifier/scenarios/dataset_discovery/vizualni-admin/graphql/devtools.ts`
126+
- `/amplifier/scenarios/dataset_discovery/vizualni-admin/graphql/client.tsx`
127+
- `/amplifier/scenarios/dataset_discovery/vizualni-admin/graphql/index.ts`
128+
129+
### Modified Files
130+
- `/amplifier/scenarios/dataset_discovery/vizualni-admin/next.config.js`
131+
- `/amplifier/scenarios/dataset_discovery/vizualni-admin/tsconfig.json`
132+
133+
## Benefits
134+
1. **CI/CD Compatibility**: Works reliably in all build environments
135+
2. **Development Experience**: Enhanced debugging with devtools in development
136+
3. **Zero Production Impact**: DevTools completely disabled in production
137+
4. **Type Safety**: Full TypeScript support throughout
138+
5. **Error Resilience**: Robust error handling and retry logic
139+
6. **Environment Flexibility**: Works with different GraphQL endpoints per environment
140+
141+
## Verification Commands
142+
```bash
143+
# Test build
144+
npm run build
145+
146+
# Test development
147+
npm run dev
148+
149+
# Test TypeScript compilation
150+
npx tsc --noEmit
151+
```
152+
153+
This fix permanently resolves the `@/graphql/devtools` module resolution issue and provides a robust GraphQL client that works consistently across all environments.
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/**
2+
* GraphQL Client
3+
*
4+
* A robust GraphQL client that works across environments with proper error handling
5+
* and optional devtools integration for development.
6+
*/
7+
8+
import { devtools, GraphQLRequest, GraphQLResponse } from './devtools';
9+
10+
interface GraphQLClientOptions {
11+
endpoint: string;
12+
headers?: Record<string, string>;
13+
timeout?: number;
14+
retries?: number;
15+
enableDevTools?: boolean;
16+
}
17+
18+
class GraphQLClient {
19+
private endpoint: string;
20+
private headers: Record<string, string>;
21+
private timeout: number;
22+
private retries: number;
23+
private middleware?: ReturnType<typeof devtools.createDevToolsMiddleware>;
24+
25+
constructor(options: GraphQLClientOptions) {
26+
this.endpoint = options.endpoint;
27+
this.headers = {
28+
'Content-Type': 'application/json',
29+
...options.headers,
30+
};
31+
this.timeout = options.timeout || 10000;
32+
this.retries = options.retries || 3;
33+
34+
// Enable devtools if requested and available
35+
if (options.enableDevTools !== false && devtools.getEnabled()) {
36+
this.middleware = devtools.createDevToolsMiddleware();
37+
}
38+
}
39+
40+
private async fetchWithTimeout(
41+
url: string,
42+
options: RequestInit,
43+
timeout: number
44+
): Promise<Response> {
45+
const controller = new AbortController();
46+
const timeoutId = setTimeout(() => controller.abort(), timeout);
47+
48+
try {
49+
const response = await fetch(url, {
50+
...options,
51+
signal: controller.signal,
52+
});
53+
clearTimeout(timeoutId);
54+
return response;
55+
} catch (error) {
56+
clearTimeout(timeoutId);
57+
if (error instanceof Error && error.name === 'AbortError') {
58+
throw new Error(`Request timeout after ${timeout}ms`);
59+
}
60+
throw error;
61+
}
62+
}
63+
64+
private async makeRequest(request: GraphQLRequest): Promise<GraphQLResponse> {
65+
const body = JSON.stringify(request);
66+
67+
// Log request if devtools are enabled
68+
this.middleware?.onRequest(request);
69+
70+
const response = await this.fetchWithTimeout(
71+
this.endpoint,
72+
{
73+
method: 'POST',
74+
headers: this.headers,
75+
body,
76+
},
77+
this.timeout
78+
);
79+
80+
if (!response.ok) {
81+
const errorText = await response.text();
82+
const error = new Error(
83+
`GraphQL request failed: ${response.status} ${response.statusText} - ${errorText}`
84+
);
85+
86+
// Log error if devtools are enabled
87+
this.middleware?.onError(error, request);
88+
89+
throw error;
90+
}
91+
92+
let data: GraphQLResponse;
93+
94+
try {
95+
data = await response.json();
96+
} catch (parseError) {
97+
const error = new Error(`Failed to parse GraphQL response: ${parseError}`);
98+
99+
// Log error if devtools are enabled
100+
this.middleware?.onError(error as Error, request);
101+
102+
throw error;
103+
}
104+
105+
// Log response if devtools are enabled
106+
this.middleware?.onResponse(data, request);
107+
108+
return data;
109+
}
110+
111+
public async request(request: GraphQLRequest): Promise<GraphQLResponse> {
112+
let lastError: Error | null = null;
113+
114+
for (let attempt = 0; attempt <= this.retries; attempt++) {
115+
try {
116+
return await this.makeRequest(request);
117+
} catch (error) {
118+
lastError = error as Error;
119+
120+
// Don't retry on client errors (4xx) or if this is the last attempt
121+
if (error instanceof Error &&
122+
(error.message.includes('400') || error.message.includes('401') || error.message.includes('403') || error.message.includes('404')) ||
123+
attempt === this.retries) {
124+
throw error;
125+
}
126+
127+
// Wait before retrying (exponential backoff)
128+
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
129+
await new Promise(resolve => setTimeout(resolve, delay));
130+
}
131+
}
132+
133+
throw lastError;
134+
}
135+
136+
public async query(query: string, variables?: Record<string, any>): Promise<any> {
137+
const response = await this.request({
138+
query,
139+
variables,
140+
operationName: this.extractOperationName(query),
141+
});
142+
143+
if (response.errors) {
144+
throw new Error(
145+
`GraphQL errors: ${response.errors.map(err => err.message).join(', ')}`
146+
);
147+
}
148+
149+
return response.data;
150+
}
151+
152+
public async mutate(
153+
mutation: string,
154+
variables?: Record<string, any>
155+
): Promise<any> {
156+
const response = await this.request({
157+
query: mutation,
158+
variables,
159+
operationName: this.extractOperationName(mutation),
160+
});
161+
162+
if (response.errors) {
163+
throw new Error(
164+
`GraphQL errors: ${response.errors.map(err => err.message).join(', ')}`
165+
);
166+
}
167+
168+
return response.data;
169+
}
170+
171+
private extractOperationName(query: string): string | undefined {
172+
// Simple regex to extract operation name from GraphQL query
173+
const match = query.match(/(?:query|mutation|subscription)\s+(\w+)/i);
174+
return match ? match[1] : undefined;
175+
}
176+
177+
// Utility method to set headers dynamically
178+
public setHeader(key: string, value: string): void {
179+
this.headers[key] = value;
180+
}
181+
182+
// Utility method to remove headers
183+
public removeHeader(key: string): void {
184+
delete this.headers[key];
185+
}
186+
187+
// Get current configuration
188+
public getConfig(): Omit<GraphQLClientOptions, 'enableDevTools'> {
189+
return {
190+
endpoint: this.endpoint,
191+
headers: { ...this.headers },
192+
timeout: this.timeout,
193+
retries: this.retries,
194+
};
195+
}
196+
}
197+
198+
// Create a default client instance if we have a GraphQL endpoint configured
199+
let defaultClient: GraphQLClient | null = null;
200+
201+
export function createGraphQLClient(options: GraphQLClientOptions): GraphQLClient {
202+
return new GraphQLClient(options);
203+
}
204+
205+
export function getDefaultGraphQLClient(): GraphQLClient | null {
206+
return defaultClient;
207+
}
208+
209+
export function setDefaultGraphQLClient(client: GraphQLClient): void {
210+
defaultClient = client;
211+
}
212+
213+
// Initialize default client if environment variables are available
214+
if (typeof process !== 'undefined' && process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT) {
215+
defaultClient = createGraphQLClient({
216+
endpoint: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT,
217+
enableDevTools: true,
218+
});
219+
}
220+
221+
export { GraphQLClient };
222+
export type { GraphQLClientOptions };

0 commit comments

Comments
 (0)