Skip to content

[Optional] Internalize RWGPS Library with Core/Adapter Pattern #175

@TobyHFerguson

Description

@TobyHFerguson

Overview

Optional: Consider internalizing RWGPSLib into the codebase following Core/Adapter pattern.

Decision Criteria

Should only proceed if:

  • ✅ You need features RWGPSLib doesn't provide
  • ✅ You're comfortable maintaining OAuth/API code
  • ✅ RWGPSLib has caused production issues
  • ✅ You want extensive testing around RWGPS operations

Skip if:

  • ❌ External library works fine (less maintenance burden)
  • ❌ No specific pain points with current implementation

Proposed Architecture

// RWGPSCore.js - Pure JavaScript
class RWGPSCore {
    static prepareEventPayload(event) { 
        // Pure JSON building logic
        // 100% testable
    }
    
    static parseEventResponse(json) {
        // Pure response parsing
        // 100% testable
    }
    
    static validateEventData(event) {
        // Pure validation
        // 100% testable
    }
}

// RWGPSAdapter.js - GAS API calls
class RWGPSAdapter {
    constructor(apiKey) { 
        this.apiKey = apiKey; 
    }
    
    getEvent(url) {
        const response = UrlFetchApp.fetch(url + '.json');
        return RWGPSCore.parseEventResponse(
            JSON.parse(response.getContentText())
        );
    }
}

Success Criteria (If Proceeding)

  • RWGPSLib code copied into src/
  • Comprehensive type definitions in Externals.d.ts
  • RWGPSCore.js with pure business logic
  • RWGPSCore.test.js with 100% coverage
  • RWGPSAdapter.js as thin GAS wrapper
  • RideManager updated to use RWGPSAdapter
  • All RWGPS operations tested
  • Zero regression in production

Effort Estimate

4-7 days

Dependencies

CRITICAL: Must complete Phase 1 (Issues #171, #172, #173) BEFORE considering this

Recommendation

Defer this decision until Phase 1 complete. External library is likely fine.

References

  • Architecture Plan: docs/Architecture-Refactoring-Plan.md
  • Copilot Instructions: .github/copilot-instructions.md

Note on This Issue

This is an optional/deferred issue. Recommendation is to skip unless there's a clear need.

The external RWGPSLib library is working well, and internalizing it adds maintenance burden. Only proceed if:

  • RWGPSLib is causing production issues
  • You need features it doesn't provide
  • You want extensive testing around RWGPS operations

Copilot Execution Checklist

Before Starting

  • Read .github/copilot-instructions.md sections: Architecture Overview, Module Addition Checklist, TypeScript Type Coverage
  • Understand Core vs Adapter separation pattern (Core = pure JS with 100% tests, Adapter = thin GAS wrapper)
  • CRITICAL: Understand GAS conditional module pattern (works in BOTH Jest and GAS)

GAS-Compatible Module Pattern

MANDATORY: All modules must work in BOTH Jest (with require/module.exports) AND GAS (no modules, global scope):

// At top of file - Jest compatibility
if (typeof require !== 'undefined') {
    var SomeDependency = require('./SomeDependency');  // Use 'var', not 'const'!
}

// Module definition using IIFE
var ModuleName = (function() {
    class ModuleName {
        // Implementation
    }
    return ModuleName;
})();

// At bottom - Jest export
if (typeof module !== 'undefined') {
    module.exports = ModuleName;  // Export the class/object
}

Why this works:

  • if (typeof require !== 'undefined') - Only runs in Jest (Node.js environment)
  • var declarations - Hoisted in GAS, can be redeclared without error
  • IIFE pattern - Works in both environments
  • if (typeof module !== 'undefined') - Only runs in Jest for exports
  • In GAS: All files concatenated to global scope, ModuleName becomes global variable

Implementation Steps

  1. Create Core module - src/ModuleNameCore.js

    • Write pure JavaScript business logic (NO GAS APIs)
    • Use dependency injection pattern (pass dependencies as parameters)
    • Follow conditional module pattern above
    • Variable name MUST match class/module name
  2. Create tests FIRST - test/__tests__/ModuleNameCore.test.js

    • Achieve 100% coverage BEFORE writing adapter
    • Run: npm test -- --coverage --collectCoverageFrom='src/ModuleNameCore.js'
    • Must show 100% statements/branches/functions/lines
    • Only acceptable uncovered: if (typeof require !== 'undefined') compatibility checks
  3. Create type definitions - src/ModuleNameCore.d.ts

    • Add JSDoc comments to all functions in .js file
    • Use declare namespace for namespace objects (e.g., var X = { method1: ..., method2: ... })
    • Use declare class for class constructors (e.g., class X { ... })
    • Export default: export default ModuleNameCore;
  4. Update Exports.js - Add module to src/Exports.js

    get ModuleNameCore() {
        return ModuleNameCore;  // Property name MUST match variable name
    }
    • Run: npm run validate-exports (must pass - verifies module is defined)
  5. MANDATORY: Check VS Code errors

    • Run: get_errors(['src/ModuleNameCore.js', 'src/ModuleNameCore.d.ts'])
    • Fix ALL errors before proceeding (zero tolerance)
    • CRITICAL: VS Code TypeScript server is MORE STRICT than tsc --noEmit
    • Common fixes:
      • Add /** @type {any[]} */ for array variables
      • Add /** @param {T} paramName */ to all function parameters
      • Add error type guards: const err = error instanceof Error ? error : new Error(String(error));
  6. Refactor adapter (if updating existing file) - src/ModuleName.js

    • Keep ONLY GAS API calls (SpreadsheetApp, PropertiesService, etc.)
    • Delegate all logic to Core module
    • Update corresponding .d.ts file
    • Run get_errors again
  7. Final validation

    npm run typecheck && npm run validate-exports && npm test
    • ALL must pass with ZERO errors
  8. Deploy and test

    npm run dev:push
    • Manual testing in GAS environment
    • Check spreadsheet menu: "Ride Schedulers" > "Show Version" to verify deployment
    • Check User Activity Log for any errors
    • Test the specific functionality you modified

Completion Criteria

  • All checkboxes in "Success Criteria" section checked
  • Zero VS Code errors: get_errors(['src/']) shows none
  • 100% test coverage for Core module
  • npm run typecheck passes (ZERO errors)
  • npm run validate-exports passes
  • npm test passes (all tests green)
  • Manual testing successful in dev environment
  • No regressions in production functionality

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrefactoringCode refactoring for maintainability

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions