From 5a90956db8a9508bb1ae5ded31b42ea46a714998 Mon Sep 17 00:00:00 2001 From: Henk-Jan Lebbink Date: Mon, 29 Sep 2025 14:40:00 +0200 Subject: [PATCH] fix: update HTTP header constants to use proper capitalization (#186) - Update all X-Amz-* headers to use capital letters after hyphens (e.g., X-Amz-Date) - Update standard HTTP headers to use proper case (e.g., If-Modified-Since) - Update X-Minio headers to follow same capitalization pattern - Aligns with AWS S3 API specification and HTTP standards --- AGENTS.md | 1 + CLAUDE.md | 227 ++++++++++++++++++++++++++++++++++++ src/s3/header_constants.rs | 68 +++++------ tests/run-tests-windows.ps1 | 4 +- 4 files changed, 264 insertions(+), 36 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..681311e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..274c6c1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,227 @@ +# Claude Code Style Guide for MinIO Rust SDK + +- Only provide actionable feedback. +- Exclude code style comments on generated files. These will have a header signifying that. +- Use github markdown folded sections for all items. +- Do not use emojis. +- Do not add a "feel good" section. + +## Copyright Header + +All source files that haven't been generated MUST include the following copyright header: + +```rust +// MinIO Rust Library for Amazon S3 Compatible Cloud Storage +// Copyright 20?? MinIO, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +``` + +## Code Style Guidelines + +### Ignore files +- Ignore files from processing mentioned under '.gitignore' + +### Comments +- **NO redundant comments** - Code should be self-documenting +- Avoid obvious comments like `// Set x to 5` for `x := 5` +- Only add comments when they explain WHY, not WHAT +- Document complex algorithms or non-obvious business logic + +## Critical Code Patterns + +### Builder Pattern +All S3 API requests MUST use the builder pattern, with the following documentation but then for the appropriate API + +```rust +/// Argument builder for the [`AppendObject`](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-buckets-objects-append.html) S3 API operation. +/// +/// This struct constructs the parameters required for the [`Client::append_object`](crate::s3::client::Client::append_object) method. +``` + +**Key Requirements:** +1. The aws docs url must exist. + +### Error Handling Pattern +All Rust SDK methods should follow consistent error handling patterns: + +```rust +impl Client { + pub async fn operation_name(&self, args: &OperationArgs) -> Result { + // Validate inputs early + args.validate()?; + + // Build request + let request = self.build_request(args)?; + + // Execute with proper error propagation + let response = self.execute(request).await?; + + // Parse and return + OperationResponse::from_response(response) + } +} +``` + +## Code Quality Principles + +### Why Code Quality Standards Are Mandatory + +Code quality standards are **critical business requirements** for MinIO Rust SDK: + +1. **Enterprise Data Safety**: A single bug can corrupt terabytes of customer data across distributed systems +2. **Code Efficiency**: MinIO Rust SDK code must be efficient and performant +3. **Scalability**: MinIO Rust SDK must be able to handle thousands of concurrent requests +4. **High Availability**: Systems must handle failures gracefully - unpredictable code creates cascading failures +5. **Developer Velocity**: New team members must understand complex distributed systems quickly and safely + +### Predictable Code Requirements + +Code must exhibit **deterministic behavior** to ensure system reliability: + +1. **Managed State**: Use Arc> or Arc> for shared state that needs thread-safe access across async operations +2. **Explicit Dependencies**: Business logic dependencies should be passed as parameters or dependency injection +3. **Deterministic Operations**: Avoid time-dependent logic, random values, or platform-specific behavior in core paths +4. **Consistent Error Handling**: Same error conditions must always produce identical error responses +5. **Idempotent Operations**: Operations should be safely repeatable without unintended side effects + +### Readability Standards + +Complex distributed systems code must remain **human-readable**: + +1. **Self-Documenting Code**: Function and variable names should clearly express business intent +2. **Consistent Patterns**: Follow established patterns (HTTP handlers, error handling, logging) +3. **Logical Flow**: Code should read as a clear narrative from top to bottom +4. **Minimal Cognitive Load**: Each function should have a single, well-defined responsibility +5. **Clear Abstractions**: Break complex operations into well-named, focused helper functions + +### Separation of Concerns + +**Architectural layers must maintain clear boundaries**: + +1. **Handler Layer**: HTTP request/response processing, input validation, context creation +2. **Service Layer**: Business logic orchestration, data transformation +3. **Storage Layer**: Data persistence, replication, consistency management +4. **Utility Layer**: Reusable helpers with no business logic dependencies +5. **Shared State Coordination**: Use thread-safe primitives (Arc, Mutex, RwLock) for components needing consistent views + +### Functions and Methods +- Keep functions focused on a single responsibility +- Use descriptive names that clearly indicate purpose and business intent +- Prefer early returns to reduce nesting complexity +- Error handling should be immediate and explicit +- **Function length guideline**: Most functions should be under 100 lines; handlers may be longer due to validation logic +- **Parameter limits**: Prefer structs over long parameter lists for better maintainability + +### Variables +- Use meaningful variable names that reflect business concepts +- Variable names should reflect usage frequency: frequent variables can be shorter +- Constants should follow Rust patterns +- Global variables should be clearly identified and documented for their system-wide purpose + +### Developer Documentation + +**All significant features must include developer documentation** in the `docs/` directory: + +1. **API Documentation**: New endpoints must have usage examples in `docs/` +2. **Architecture Decisions**: Complex algorithms or design patterns should be documented +3. **Configuration Changes**: New config options must be documented with examples +4. **Integration Guides**: External system integrations need clear setup instructions +5. **Future Developer Context**: Document WHY decisions were made, not just WHAT was implemented + +## Testing Requirements + +### Why Unit Tests Are Mandatory + +Unit tests are **non-negotiable** in this project for critical business reasons: + +1. **Data Integrity**: MinIO Rust SDK handles enterprise-critical data. A single bug can cause data loss affecting thousands of users +2. **Security Compliance**: Financial and healthcare customers require verifiable code quality. Tests provide audit trails +3. **Multi-tenant Reliability**: One customer's workload cannot impact another's. Tests ensure proper isolation +4. **Performance SLAs**: Enterprise customers have strict performance requirements. Tests validate behavior under load +5. **API Stability**: Breaking changes can affect thousands of applications. Tests prevent regressions +6. **Distributed System Complexity**: Complex interactions between storage nodes require comprehensive testing + +### Mandatory Unit Tests +**EVERY implementation MUST include unit tests** without being explicitly asked. Follow these patterns: + +1. Test functions must use `#[test]` or `#[tokio::test]` attributes +2. Use parameterized tests or loop through test cases for multiple scenarios +3. Cover both success and error cases, including edge conditions +4. Mock external dependencies appropriately +5. **Test coverage guideline**: Aim for comprehensive coverage of new code paths +6. Include negative tests for error conditions and boundary cases +7. Add benchmarks for performance-critical code paths + +## Improvement Suggestions + +Claude will periodically analyze the codebase and suggest: +- Missing test coverage areas +- Performance optimizations +- Code refactoring opportunities +- Security improvements +- Documentation gaps + +## Testing Commands + +### Pre-commit Checklist + +Before any code changes: +1. ✅ Run `cargo fmt --all` to check and fix code formatting +2. ✅ Run `cargo test` to ensure all tests pass +3. ✅ Run `cargo clippy` to check for common mistakes +4. ✅ Ensure new code has appropriate test coverage +5. ✅ Verify no redundant comments are added + +## Directory Structure Conventions + +- `/src` - Main library source code +- `/tests` - Integration tests +- `/examples` - Example usage code +- `/docs` - Documentation +- `/benches` - Performance benchmarks + +## Common Patterns to Follow + +### Logging +Use the log crate with appropriate macros: + +```rust +use log::{debug, error, info, trace, warn}; + +// Examples: +info!("Starting operation: {}", operation_name); +debug!("Request details: {:?}", request); +error!("Operation failed: {}", err); +``` + +### Error Handling +Use the `Result` type with proper error propagation: + +```rust +use crate::s3::error::Error; + +fn operation() -> Result { + let result = risky_operation()?; + Ok(process(result)) +} +``` + +## Quick Reference + +- **Fix formatting**: `cargo fmt --all` +- **Run tests**: `cargo test` +- **Run specific test**: `cargo test test_name` +- **Check code**: `cargo clippy` +- **Build project**: `cargo build --release` +- **Generate docs**: `cargo doc --open` diff --git a/src/s3/header_constants.rs b/src/s3/header_constants.rs index 7574c9f..56dbfbf 100644 --- a/src/s3/header_constants.rs +++ b/src/s3/header_constants.rs @@ -13,10 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub const IF_MATCH: &str = "if-match"; -pub const IF_NONE_MATCH: &str = "if-none-match"; -pub const IF_MODIFIED_SINCE: &str = "if-modified-since"; -pub const IF_UNMODIFIED_SINCE: &str = "if-unmodified-since"; +pub const IF_MATCH: &str = "If-Match"; +pub const IF_NONE_MATCH: &str = "If-None-Match"; +pub const IF_MODIFIED_SINCE: &str = "If-Modified-Since"; +pub const IF_UNMODIFIED_SINCE: &str = "If-Unmodified-Since"; pub const CONTENT_MD5: &str = "Content-MD5"; pub const CONTENT_TYPE: &str = "Content-Type"; pub const AUTHORIZATION: &str = "Authorization"; @@ -26,61 +26,61 @@ pub const CONTENT_LENGTH: &str = "Content-Length"; pub const POLICY: &str = "policy"; -pub const X_MINIO_DEPLOYMENT_ID: &str = "x-minio-deployment-id"; +pub const X_MINIO_DEPLOYMENT_ID: &str = "X-Minio-Deployment-Id"; -pub const X_AMZ_VERSION_ID: &str = "x-amz-version-id"; -pub const X_AMZ_ID_2: &str = "x-amz-id-2"; -pub const X_AMZ_WRITE_OFFSET_BYTES: &str = "x-amz-write-offset-bytes"; +pub const X_AMZ_VERSION_ID: &str = "X-Amz-Version-Id"; +pub const X_AMZ_ID_2: &str = "X-Amz-Id-2"; +pub const X_AMZ_WRITE_OFFSET_BYTES: &str = "X-Amz-Write-Offset-Bytes"; -pub const X_AMZ_OBJECT_SIZE: &str = "x-amz-object-size"; -pub const X_AMZ_TAGGING: &str = "x-amz-tagging"; +pub const X_AMZ_OBJECT_SIZE: &str = "X-Amz-Object-Size"; +pub const X_AMZ_TAGGING: &str = "X-Amz-Tagging"; -pub const X_AMZ_BUCKET_REGION: &str = "x-amz-bucket-region"; +pub const X_AMZ_BUCKET_REGION: &str = "X-Amz-Bucket-Region"; -pub const X_AMZ_OBJECT_LOCK_MODE: &str = "x-amz-object-lock-mode"; +pub const X_AMZ_OBJECT_LOCK_MODE: &str = "X-Amz-Object-Lock-Mode"; -pub const X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE: &str = "x-amz-object-lock-retain-until-date"; +pub const X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE: &str = "X-Amz-Object-Lock-Retain-Until-Date"; -pub const X_AMZ_OBJECT_LOCK_LEGAL_HOLD: &str = "x-amz-object-lock-legal-hold"; +pub const X_AMZ_OBJECT_LOCK_LEGAL_HOLD: &str = "X-Amz-Object-Lock-Legal-Hold"; -pub const X_AMZ_METADATA_DIRECTIVE: &str = "x-amz-metadata-directive"; +pub const X_AMZ_METADATA_DIRECTIVE: &str = "X-Amz-Metadata-Directive"; -pub const X_AMZ_TAGGING_DIRECTIVE: &str = "x-amz-tagging-directive"; +pub const X_AMZ_TAGGING_DIRECTIVE: &str = "X-Amz-Tagging-Directive"; -pub const X_AMZ_COPY_SOURCE: &str = "x-amz-copy-source"; +pub const X_AMZ_COPY_SOURCE: &str = "X-Amz-Copy-Source"; -pub const X_AMZ_COPY_SOURCE_RANGE: &str = "x-amz-copy-source-range"; +pub const X_AMZ_COPY_SOURCE_RANGE: &str = "X-Amz-Copy-Source-Range"; -pub const X_AMZ_COPY_SOURCE_IF_MATCH: &str = "x-amz-copy-source-if-match"; +pub const X_AMZ_COPY_SOURCE_IF_MATCH: &str = "X-Amz-Copy-Source-If-Match"; -pub const X_AMZ_COPY_SOURCE_IF_NONE_MATCH: &str = "x-amz-copy-source-if-none-match"; +pub const X_AMZ_COPY_SOURCE_IF_NONE_MATCH: &str = "X-Amz-Copy-Source-If-None-Match"; -pub const X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE: &str = "x-amz-copy-source-if-unmodified-since"; +pub const X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE: &str = "X-Amz-Copy-Source-If-Unmodified-Since"; -pub const X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE: &str = "x-amz-copy-source-if-modified-since"; +pub const X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE: &str = "X-Amz-Copy-Source-If-Modified-Since"; -pub const X_AMZ_BUCKET_OBJECT_LOCK_ENABLED: &str = "x-amz-bucket-object-lock-enabled"; +pub const X_AMZ_BUCKET_OBJECT_LOCK_ENABLED: &str = "X-Amz-Bucket-Object-Lock-Enabled"; -pub const X_AMZ_BYPASS_GOVERNANCE_RETENTION: &str = "x-amz-bypass-governance-retention"; +pub const X_AMZ_BYPASS_GOVERNANCE_RETENTION: &str = "X-Amz-Bypass-Governance-Retention"; -pub const X_AMZ_DATE: &str = "x-amz-date"; +pub const X_AMZ_DATE: &str = "X-Amz-Date"; -pub const X_AMZ_DELETE_MARKER: &str = "x-amz-delete-marker"; -pub const X_AMZ_ALGORITHM: &str = "x-amz-algorithm"; +pub const X_AMZ_DELETE_MARKER: &str = "X-Amz-Delete-Marker"; +pub const X_AMZ_ALGORITHM: &str = "X-Amz-Algorithm"; -pub const X_AMZ_CREDENTIAL: &str = "x-amz-credential"; +pub const X_AMZ_CREDENTIAL: &str = "X-Amz-Credential"; -pub const X_AMZ_SIGNATURE: &str = "x-amz-signature"; +pub const X_AMZ_SIGNATURE: &str = "X-Amz-Signature"; -pub const X_AMZ_REQUEST_ID: &str = "x-amz-request-id"; +pub const X_AMZ_REQUEST_ID: &str = "X-Amz-Request-Id"; -pub const X_AMZ_EXPIRES: &str = "x-amz-expires"; +pub const X_AMZ_EXPIRES: &str = "X-Amz-Expires"; -pub const X_AMZ_SIGNED_HEADERS: &str = "x-amz-signedheaders"; +pub const X_AMZ_SIGNED_HEADERS: &str = "X-Amz-SignedHeaders"; -pub const X_AMZ_CONTENT_SHA256: &str = "x-amz-content-sha256"; +pub const X_AMZ_CONTENT_SHA256: &str = "X-Amz-Content-SHA256"; -pub const X_AMZ_SECURITY_TOKEN: &str = "x-amz-security-token"; +pub const X_AMZ_SECURITY_TOKEN: &str = "X-Amz-Security-Token"; pub const X_AMZ_SERVER_SIDE_ENCRYPTION: &str = "X-Amz-Server-Side-Encryption"; diff --git a/tests/run-tests-windows.ps1 b/tests/run-tests-windows.ps1 index f1a01ae..8c2a7f2 100644 --- a/tests/run-tests-windows.ps1 +++ b/tests/run-tests-windows.ps1 @@ -1,7 +1,7 @@ # Set environment variables to run tests on play.min.io $Env:SERVER_ENDPOINT = "http://localhost:9000/" -$Env:ACCESS_KEY = "minioadmin" -$Env:SECRET_KEY = "minioadmin" +$Env:ACCESS_KEY = if ($Env:MINIO_ROOT_USER) { $Env:MINIO_ROOT_USER } else { "minioadmin" } +$Env:SECRET_KEY = if ($Env:MINIO_ROOT_PASSWORD) { $Env:MINIO_ROOT_PASSWORD } else { "minioadmin" } $Env:ENABLE_HTTPS = "false" $Env:MINIO_SSL_CERT_FILE = "./tests/public.crt" $Env:IGNORE_CERT_CHECK = "false"