-
Notifications
You must be signed in to change notification settings - Fork 309
Description
Problem
The Upload extension currently uses runtime type checking (instanceof Blob) to detect and extract files from variables. This has several limitations:
- Inefficient: Blindly traverses entire variable structure regardless of schema
- Incomplete detection: Only checks top-level variables (would miss
{ input: { avatar: blob } }) - Inconsistent: Doesn't follow the same pattern as custom scalar encoding
- No schema awareness: Doesn't leverage GraphQL type information
Current Implementation
Upload.ts:66-69 - Shallow detection:
const isUploadRequest = (request: RequestAnalyzedInput) => {
if (!request.variables) return false
return Object.values(request.variables).some(_ => _ instanceof Blob)
}extractFiles.ts - Traverses everything:
- Recursively visits every array, object, and value
- No schema guidance on where Upload scalars are located
- Performance: O(all nodes in variables)
Existing Pattern: Custom Scalar Encoding
Graffle already has schema-driven variable traversal for custom scalar encoding!
src/requestPipeline/encode.ts:7-87 demonstrates the pattern:
- Accesses SDDM (Schema-Driven Data Map) from
input.state.configuration.schema.current.map - Walks variable definitions from the GraphQL operation
- Looks up types in SDDM
- Visits only fields containing custom scalars (
sddmNode.fcs) - Performance: O(custom scalar paths only)
This is exactly what Upload needs, but for Upload scalar types instead of all custom scalars.
Proposed Solution: Simple API
Ideal Extension Author API
Extension.create('Upload')
.requestInterceptor(async ({ pack }) => {
// Get all variables of type 'Upload'
const uploadFiles = visitVariableTypes(pack.input, ['Upload'])
// Returns: Map<Blob, string[]> - each Upload value and its paths
if (uploadFiles.size === 0) return pack()
return pack({
using: {
body: () => createMultipartBody(uploadFiles, pack.input.request)
},
input: {
...pack.input,
transport: {
...pack.input.transport,
headers: { 'content-type': '' }
}
}
})
})Implementation Signature
/**
* Extract all variables matching specified GraphQL type names.
* Uses schema information (SDDM) to efficiently visit only relevant paths.
*
* @param packInput - Request pipeline pack input containing request, state, variables
* @param typeNames - GraphQL type names to match (e.g., ['Upload', 'File'])
* @returns Map of values to their paths within variables (e.g., "input.avatar", "attachments.0")
*
* @example
* const uploadFiles = visitVariableTypes(pack.input, ['Upload'])
* // Map { <Blob> => ["variables.input.avatar", "variables.documents.0"] }
*/
export const visitVariableTypes = (
packInput: {
request: Grafaid.RequestAnalyzedInput
state: Context
},
typeNames: string[]
): Map<any, string[]>Key Design Principles
- Simple - Extension authors just specify type names
- Schema-driven - Leverages SDDM internally (hidden complexity)
- Efficient - Only visits paths that match specified types
- Consistent - Same pattern as
encodeRequestVariables - Type-safe - Returns clean Map structure
Implementation Plan
1. Create Shared Utility Module
Location: src/requestPipeline/visitVariableTypes.ts
Based on encodeRequestVariables pattern but generalized:
- Extract SDDM from
state.configuration.schema.current.map - Get variable definitions from
request.operation.variableDefinitions - For each variable, look up its type in SDDM
- Recursively visit structure, checking if types match
typeNames - Collect matching values and their paths
- Return
Map<any, string[]>
2. Export from Extension API
Location: src/exports/extension_exports.ts
export { visitVariableTypes } from '../requestPipeline/visitVariableTypes.js'3. Rewrite Upload Extension
Location: src/extensions/Upload/Upload.ts
- Replace runtime
instanceof Blobdetection with schema-drivenvisitVariableTypes - Simplify
extractFiles.tsor remove it entirely (logic moves tovisitVariableTypes) - Update
createBody.tsto work with Map returned byvisitVariableTypes
4. Update Tests
Location: src/extensions/Upload/Upload.test.ts
- Add tests for nested Upload scalars (currently likely untested)
- Add tests for multiple Upload scalars in same request
- Add tests for schema without Upload scalar (graceful fallback)
Benefits
| Aspect | Current (runtime check) | Proposed (schema-driven) |
|---|---|---|
| Performance | O(all nodes) | O(Upload paths only) |
| Accuracy | Shallow, may miss nested | Complete, schema-guided |
| Consistency | Different from encoding | Same pattern as encoding |
| Schema required | No | Yes (but already available) |
| Type safety | Runtime only | Schema contract enforced |
Additional Context
SDDM Structure Reference
Extensions using visitVariableTypes don't need to understand SDDM internals, but for implementation:
sddm.types[typeName]- Look up type by nameInputObject.fcs- Field names containing custom scalarsInputObject.f- Field definitionsArgumentOrInputField.nt- Named type referenceArgumentOrInputField.it- Inline type (nullability/list info)
Type Guards (Already Exist)
From src/docpar/core/sddm/SchemaDrivenDataMap.ts:253-291:
isEnum()isCustomScalarName()isScalar()isInputObject()isOutputField()
These can be used internally by visitVariableTypes.
Related Code
- Encoding pattern:
src/requestPipeline/encode.ts:7-87 - SDDM types:
src/docpar/core/sddm/SchemaDrivenDataMap.ts - Current Upload:
src/extensions/Upload/Upload.ts - Current extraction:
src/extensions/Upload/extractFiles.ts - Extension builder:
src/context/fragments/extensions/dataType/builder.ts
Acceptance Criteria
-
visitVariableTypesutility implemented and exported - Upload extension rewritten to use
visitVariableTypes - Tests pass including new nested Upload scalar tests
- Performance improvement measurable for large variable structures
- Documentation updated with extension API example
- No breaking changes to Upload extension public API
Future Extensions
This pattern enables other extensions that need type-aware variable processing:
- File validation extension (check file size/type based on schema directives)
- Variable encryption extension (encrypt specific scalar types)
- Logging extension (redact sensitive scalar types)
Priority: Medium
Labels: enhancement, extension-system, upload
Estimated Effort: 1-2 days