Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
name: Bug Report
about: Create a report to help us improve
title: '[BUG] '
labels: bug
assignees: ''
---

## Bug Description
A clear and concise description of what the bug is.

## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error

## Expected Behavior
A clear and concise description of what you expected to happen.

## Actual Behavior
What actually happened.

## Error Messages
Please paste any error messages from the Apps Script execution logs:
```
Paste error messages here
```

## Configuration
Please provide your configuration (redact actual calendar IDs for privacy):
- Number of source calendars:
- SYNC_DAYS_IN_PAST:
- SYNC_DAYS_IN_FUTURE:
- Trigger type (time-based or calendar-based):

## Environment
- Google Apps Script Runtime: [e.g., V8]
- Timezone in appsscript.json: [e.g., America/Chicago]
- When did this start happening: [e.g., after update on 2024-01-01]

## Screenshots
If applicable, add screenshots to help explain your problem.

## Additional Context
Add any other context about the problem here.

## Troubleshooting Attempted
What have you already tried?
- [ ] Checked the [Troubleshooting Guide](../../TROUBLESHOOTING.md)
- [ ] Reviewed execution logs
- [ ] Verified calendar permissions
- [ ] Tested with reduced date range
- [ ] Checked API quotas
30 changes: 30 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
name: Feature Request
about: Suggest an idea for this project
title: '[FEATURE] '
labels: enhancement
assignees: ''
---

## Is your feature request related to a problem?
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

## Describe the solution you'd like
A clear and concise description of what you want to happen.

## Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

## Use Case
Describe your use case for this feature. How would it help you and others?

## Additional Context
Add any other context, screenshots, or examples about the feature request here.

## Implementation Ideas
If you have ideas about how this could be implemented, share them here.

## Would you be willing to contribute?
- [ ] I'd be willing to submit a PR for this feature
- [ ] I'd be willing to help test this feature
- [ ] I'm just suggesting an idea
23 changes: 23 additions & 0 deletions .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
name: Question
about: Ask a question about using this project
title: '[QUESTION] '
labels: question
assignees: ''
---

## Your Question
Please clearly state your question.

## What I've Tried
Please describe what you've already tried or researched:
- [ ] Read the [README](../../README.md)
- [ ] Checked the [Troubleshooting Guide](../../TROUBLESHOOTING.md)
- [ ] Searched existing issues
- [ ] Reviewed the [Setup Guide](../../docs/README.md)

## Context
Provide any relevant context about your setup or what you're trying to achieve.

## Additional Information
Add any other information that might be helpful in answering your question.
41 changes: 41 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Description

Please include a summary of the changes and which issue is fixed. Include relevant motivation and context.

Fixes # (issue)

## Type of Change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Code quality improvement (refactoring, formatting, etc.)

## How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

- [ ] Tested with single source calendar
- [ ] Tested with multiple source calendars
- [ ] Tested with time-based trigger
- [ ] Tested with calendar-based trigger
- [ ] Tested error handling
- [ ] Checked execution logs for errors

## Checklist

- [ ] My code follows the style guidelines of this project (Prettier, EditorConfig)
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings or errors
- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable)
- [ ] I have updated the CHANGELOG.md (if applicable)
- [ ] I have checked that no placeholder calendar IDs are in the code

## Additional Notes

Add any additional notes, screenshots, or context about the pull request here.
30 changes: 30 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Lint and Format Check

on:
pull_request:
branches: [ main, master ]
push:
branches: [ main, master ]

jobs:
prettier:
name: Check Prettier Formatting
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install Prettier
run: npm install --global prettier

- name: Check formatting
run: prettier --check "*.gs" "*.json" "*.md" --ignore-path .gitignore || true

- name: Show formatting diff (if any)
if: failure()
run: prettier --check "*.gs" "*.json" "*.md" --ignore-path .gitignore --list-different
68 changes: 55 additions & 13 deletions BatchRequests.gs
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
// Based on https://github.com/tanaikech/BatchRequest

// Handles batch requests to Google Calendar API for efficient bulk operations.
// Google Calendar API allows up to 100 requests per batch.

/**
* BatchRequest class for handling multiple Google Calendar API requests efficiently.
* Automatically splits large request arrays into batches of 100 (API limit).
*
* @class
* @param {Object} obj - Configuration object
* @param {Array} obj.requests - Array of request objects with method, endpoint, and optional requestBody
* @param {string} [obj.batchPath] - Optional batch API path (default: uses requests[0] API version)
* @param {string} [obj.accessToken] - Optional OAuth token (default: uses ScriptApp.getOAuthToken())
* @param {boolean} [obj.useFetchAll] - Force use of UrlFetchApp.fetchAll for parallel requests
* @returns {Array} Array of response objects from the batch requests
*
* @example
* const result = new BatchRequest({
* batchPath: "batch/calendar/v3",
* requests: [
* { method: "POST", endpoint: "...", requestBody: {...} },
* { method: "DELETE", endpoint: "..." }
* ]
* });
*/
class BatchRequest {
constructor(obj) {
if (!obj.hasOwnProperty('requests')) {
Expand Down Expand Up @@ -31,16 +54,22 @@ class BatchRequest {
}
}

/**
* Enhanced batch request handler that splits requests into chunks of 100.
* Uses UrlFetchApp.fetchAll when available for parallel processing.
*
* @returns {Array} Combined array of all batch response objects
*/
enhancedDo() {
// Google Calendar API batch limit is 100 requests per batch
const limit = 100;
const split = Math.ceil(this.reqs.length / limit);

// Use fetchAll for parallel batch processing if available
if (typeof UrlFetchApp.fetchAll === 'function') {
const reqs = [];
var i = 0;
var j = 0;

for (; 0 <= split ? j < split : j > split; i = 0 <= split ? ++j : --j) {
for (let i = 0; i < split; i++) {
const params = this.createRequest(this.reqs.splice(0, limit));
params.url = this.url;
reqs.push(params);
Expand All @@ -58,10 +87,10 @@ class BatchRequest {
return res;
}

var allResponses = [];
var i = 0;
var k = 0;
for (; 0 <= split ? k < split : k > split; i = 0 <= split ? ++k : --k) {
// Fallback: Process batches sequentially
const allResponses = [];

for (let i = 0; i < split; i++) {
const params = this.createRequest(this.reqs.splice(0, limit));

const response = UrlFetchApp.fetch(this.url, params);
Expand All @@ -78,9 +107,16 @@ class BatchRequest {
return allResponses;
}

/**
* Parses batch response content into individual response objects.
* Batch responses are separated by boundary markers.
*
* @param {string} contentText - Raw batch response text
* @returns {Array} Array of parsed response objects
*/
parser(contentText) {
const regex = /{[\S\s]+}/g;
var temp = contentText.split('--batch');
const temp = contentText.split('--batch');

return temp.slice(1, temp.length - 1).map((e) => {
if (regex.test(e)) {
Expand All @@ -90,11 +126,19 @@ class BatchRequest {
});
}

/**
* Creates a properly formatted batch request payload.
* Follows the multipart/mixed content format required by Google APIs.
*
* @param {Array} requests - Array of request objects to batch
* @returns {Object} UrlFetchApp request options object
*/
createRequest(requests) {
const boundary = 'xxxxxxxxxx';

var contentId = 0;
var data = `--${boundary}\r\n`;
let contentId = 0;
let data = `--${boundary}\r\n`;

requests.forEach((req) => {
data +=
`Content-Type: application/http\r\n` +
Expand All @@ -114,8 +158,6 @@ class BatchRequest {
}

data += `--${boundary}\r\n`;

return data;
});

return {
Expand Down
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Root README.md with quick start guide and project badges
- Comprehensive CODEBASE_REVIEW.md with detailed code quality analysis
- CHANGELOG.md to track project changes
- JSDoc comments for all functions with parameter and return value documentation
- Configuration validation function (`validateConfiguration()`) that runs before sync
- Detailed inline documentation for configuration options
- Error handling with try-catch blocks throughout codebase
- Better logging with context and timestamps
- Input validation for calendar IDs and sync day ranges
- Helpful error messages with actionable guidance
- Documentation for finding calendar IDs
- Comments explaining complex logic and design decisions
- Warnings for large sync ranges that may consume significant API quota

### Changed
- Improved error messages to be more user-friendly
- Enhanced configuration documentation with examples and recommendations
- Consistent use of `const` and `let` instead of `var` throughout codebase
- Better code organization with clearer function responsibilities
- More detailed console logging during sync operations
- Improved BatchRequests.gs documentation and code clarity

### Fixed
- Month index bug: Changed `setFullYear(2000, 01, 01)` to `setFullYear(2000, 0, 1)`
- JavaScript months are 0-indexed, so `0` is January (was incorrectly using February)
- Added null check for calendar accessibility before processing

### Deprecated
- None

### Removed
- None

### Security
- Documented OAuth scope requirements
- Added warnings about not committing real calendar IDs to version control
- Improved error handling to prevent credential exposure in logs

## [1.0.0] - Previous Release

### Added
- Initial release with calendar sync functionality
- Support for multiple source calendars
- Batch request processing
- Time-based and calendar-based triggers
- Configurable sync date ranges
- Free/busy event filtering

[Unreleased]: https://github.com/karbassi/sync-multiple-google-calendars/compare/HEAD...copilot/review-codebase-improvements
Loading