-
Notifications
You must be signed in to change notification settings - Fork 4
Add makesheet package: Google Apps Script web app for sheet timestamp tracking #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copilot
wants to merge
6
commits into
master
Choose a base branch
from
copilot/fix-74
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
eb9780f
Initial plan
Copilot 78784ca
Initial exploration and planning for makesheet package
Copilot 1c60517
Complete makesheet package implementation
Copilot b3bfb59
Add Airbnb style linting and test functions to makesheet package
Copilot ee3ee2e
Address PR feedback: fix newlines, remove main field, add JSDoc, impl…
Copilot b20f099
Fix missing newlines in JSON files (package.json, appsscript.json, .e…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| .git/** | ||
| node_modules/** |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| "extends": [ | ||
| "airbnb-base" | ||
| ], | ||
| "env": { | ||
| "es6": true | ||
| }, | ||
| "rules": { | ||
| "quotes": [ | ||
| "error", | ||
| "single", | ||
| { | ||
| "avoidEscape": true, | ||
| "allowTemplateLiterals": true | ||
| } | ||
| ], | ||
| "comma-dangle": ["error", "always-multiline"], | ||
| "no-console": "off", | ||
| "no-unused-vars": [ | ||
| "error", | ||
| { | ||
| "argsIgnorePattern": "^_", | ||
| "varsIgnorePattern": "^_" | ||
| } | ||
| ], | ||
| "no-undef": "off", | ||
| "no-underscore-dangle": [ | ||
| "error", | ||
| { | ||
| "allowAfterThis": false, | ||
| "allowAfterSuper": false, | ||
| "enforceInMethodNames": false, | ||
| "allow": ["_testDoGet", "_testCreateJsonResponse"] | ||
| } | ||
| ] | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| package-lock.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # Contributing to makesheet | ||
|
|
||
| ## Code Style | ||
|
|
||
| This project follows Airbnb JavaScript style guidelines. Use ESLint to check and fix code style: | ||
|
|
||
| ```bash | ||
| yarn lint | ||
| yarn lint-fix | ||
| ``` | ||
|
|
||
| ## Object Capability (OCap) Discipline | ||
|
|
||
| This project follows object-capability discipline to enable secure composition and testing. This means avoiding ambient authority where possible. | ||
|
|
||
| ### Ambient Authority Pattern | ||
|
|
||
| When functions need to access global APIs (like `DriveApp.getFileById`), use dependency injection to allow callers to provide these capabilities: | ||
|
|
||
| ```js | ||
| function doGet(e, io = {}) { | ||
| const { | ||
| getFileById = DriveApp.getFileById, | ||
| } = io; | ||
| // ... use getFileById instead of DriveApp.getFileById directly | ||
| } | ||
| ``` | ||
|
|
||
| This pattern provides several benefits: | ||
|
|
||
| 1. **Testability**: Callers can inject mock implementations for testing | ||
| 2. **Attenuation**: Callers can provide restricted versions of capabilities | ||
| 3. **Composability**: Functions become more modular and reusable | ||
| 4. **Security**: Reduces ambient authority and enables principle of least privilege | ||
|
|
||
| ### References | ||
|
|
||
| For more details on object-capability discipline, see the [Agoric OCap Discipline wiki](https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline). | ||
|
|
||
| ## Testing | ||
|
|
||
| Since this is Google Apps Script code, we use test functions that can be executed in the Google Apps Script debugger: | ||
|
|
||
| 1. Create test functions prefixed with underscore (e.g., `_testDoGet`) | ||
| 2. Use `console.log` to output test results | ||
| 3. Run tests by selecting the test function in the GAS editor and clicking run | ||
| 4. View results in the console output | ||
|
|
||
| This approach allows testing without external frameworks while maintaining the ability to test core functionality. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| # makesheet | ||
|
|
||
| Google Sheets timestamp service for Make-based workflows. | ||
|
|
||
| ## Overview | ||
|
|
||
| This package provides a Google Apps Script web app that returns the modification timestamp of a Google Sheet. This is useful for Make-based build systems that need to track when a sheet has been modified to trigger downstream processing. | ||
|
|
||
| ## Setup | ||
|
|
||
| 1. Install clasp globally (required for deployment, though you could add it as a devDependency if preferred): | ||
| ```bash | ||
| npm install -g @google/clasp | ||
| ``` | ||
|
|
||
| 2. Login to Google Apps Script: | ||
| ```bash | ||
| yarn login | ||
| ``` | ||
|
|
||
| 3. Create a new Google Apps Script project and note the script ID. | ||
| [See the Google Apps Script documentation](https://developers.google.com/apps-script/guides/projects) for detailed instructions on creating projects. | ||
|
|
||
| 4. Update `.clasp.json` with your script ID: | ||
| ```json | ||
| { | ||
| "scriptId": "your-script-id-here", | ||
| "rootDir": "." | ||
| } | ||
| ``` | ||
|
|
||
| 5. Push the code to Google Apps Script: | ||
| ```bash | ||
| yarn push | ||
| ``` | ||
|
|
||
| 6. Deploy as a web app and note the deployment ID: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cite (link) docs for how to deploy it
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added links to Google Apps Script documentation and clasp deployment documentation. Commit ee3ee2e |
||
| ```bash | ||
| yarn deploy | ||
| ``` | ||
| [See the clasp deployment documentation](https://developers.google.com/apps-script/guides/clasp#deployments) for more details on deployment options and web app configuration. | ||
|
|
||
| ## Usage | ||
|
|
||
| Once deployed, you can query the modification time of a Google Sheet using: | ||
|
|
||
| ```bash | ||
| curl -s "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?sheetId=YOUR_SHEET_ID" | ||
| ``` | ||
|
|
||
| This returns a JSON response: | ||
| ```json | ||
| { | ||
| "modifiedTime": "2023-12-01T15:30:45.123Z" | ||
| } | ||
| ``` | ||
|
|
||
| Or an error response: | ||
| ```json | ||
| { | ||
| "error": "sheetId parameter required" | ||
| } | ||
| ``` | ||
|
|
||
| ## Integration with Make | ||
|
|
||
| Use the provided `example-usage.mk` file as a reference for integrating with Make-based workflows. | ||
|
|
||
| ## Scripts | ||
|
|
||
| - `yarn push` - Push code to Google Apps Script (uploads your local files to the remote project) | ||
| - `yarn deploy` - Push and deploy as web app (requires DEPLOYMENT_ID env var) (creates a new web app version that can be accessed via URL) | ||
| - `yarn curl` - Test the deployed web app (requires DEPLOYMENT_ID and SHEET_ID env vars) | ||
| - `yarn login` - Login to Google Apps Script | ||
| - `yarn lint` - Run ESLint to check code style (Airbnb style) | ||
| - `yarn lint-fix` - Run ESLint and automatically fix issues | ||
|
|
||
| ## Testing | ||
|
|
||
| The code includes test functions that can be used with the Google Apps Script debugger: | ||
|
|
||
| - `_testDoGet()` - Tests the main `doGet` function with various input scenarios | ||
| - `_testCreateJsonResponse()` - Tests the JSON response creation function | ||
|
|
||
| To run tests in the Google Apps Script editor: | ||
| 1. Open your script in the Google Apps Script editor | ||
| 2. Select one of the test functions from the function dropdown | ||
| 3. Click the run button to execute the test | ||
| 4. View the output in the console | ||
|
|
||
| This approach follows the pattern suggested for testing Google Apps Script functions without requiring external test frameworks. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "webapp": { | ||
| "access": "ANYONE_ANONYMOUS", | ||
| "executeAs": "USER_DEPLOYING" | ||
| }, | ||
| "timeZone": "America/Chicago", | ||
| "dependencies": { | ||
| }, | ||
| "exceptionLogging": "STACKDRIVER", | ||
| "runtimeVersion": "V8" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Example usage of makesheet package in Makefile workflows | ||
| # | ||
| # Set these environment variables: | ||
| # DEPLOYMENT_ID = your Google Apps Script deployment ID | ||
| # SHEET_ID = the Google Sheet ID you want to track | ||
|
|
||
| # Target file to store the sheet's timestamp | ||
| SHEET_TIMESTAMP_FILE = sheet.timestamp | ||
|
|
||
| # URL for the deployed makesheet web app | ||
| SHEET_INFO_URL = https://script.google.com/macros/s/$(DEPLOYMENT_ID)/exec | ||
|
|
||
| # Create a timestamp file based on the sheet's last modification time | ||
| $(SHEET_TIMESTAMP_FILE): | ||
| curl -s "$(SHEET_INFO_URL)?sheetId=$(SHEET_ID)" | \ | ||
| jq -r '.modifiedTime' | \ | ||
| xargs -I {} touch -d {} $@ | ||
|
|
||
| # Example target that depends on the sheet timestamp | ||
| data-processing: $(SHEET_TIMESTAMP_FILE) | ||
| @echo "Processing data from sheet (last modified: $$(cat $(SHEET_TIMESTAMP_FILE)))" | ||
| # Your data processing commands here | ||
|
|
||
| # Clean up timestamp file | ||
| clean: | ||
| rm -f $(SHEET_TIMESTAMP_FILE) | ||
|
|
||
| # Check if sheet has been modified since last processing | ||
| check-sheet-status: $(SHEET_TIMESTAMP_FILE) | ||
| @if [ -f $(SHEET_TIMESTAMP_FILE) ]; then \ | ||
| echo "Sheet timestamp file exists: $$(ls -la $(SHEET_TIMESTAMP_FILE))"; \ | ||
| else \ | ||
| echo "Sheet timestamp file does not exist, will be created"; \ | ||
| fi | ||
|
|
||
| # Alternative approach: always check and update if sheet is newer | ||
| update-if-newer: | ||
| @CURRENT_TIME=$$(curl -s "$(SHEET_INFO_URL)?sheetId=$(SHEET_ID)" | jq -r '.modifiedTime'); \ | ||
| if [ ! -f $(SHEET_TIMESTAMP_FILE) ] || [ "$$(date -r $(SHEET_TIMESTAMP_FILE) -u +%Y-%m-%dT%H:%M:%S.%3NZ)" != "$$CURRENT_TIME" ]; then \ | ||
| echo "Sheet has been modified, updating timestamp"; \ | ||
| echo "$$CURRENT_TIME" | xargs -I {} touch -d {} $(SHEET_TIMESTAMP_FILE); \ | ||
| else \ | ||
| echo "Sheet has not been modified since last check"; \ | ||
| fi | ||
|
|
||
| .PHONY: data-processing clean check-sheet-status update-if-newer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "name": "makesheet", | ||
| "version": "0.1.0", | ||
| "description": "Google Sheets timestamp service for Make-based workflows", | ||
| "author": "Dan Connolly (with GitHub Copilot)", | ||
| "license": "MIT", | ||
| "scripts": { | ||
| "push": "clasp push", | ||
| "deploy": "clasp push; clasp deploy --deploymentId $DEPLOYMENT_ID", | ||
| "curl": "curl -L https://script.google.com/macros/s/$DEPLOYMENT_ID/exec?sheetId=$SHEET_ID", | ||
| "login": "clasp login", | ||
| "lint": "eslint --config .eslintrc.json --no-eslintrc *.js", | ||
| "lint-fix": "eslint --config .eslintrc.json --no-eslintrc --fix *.js" | ||
| }, | ||
| "devDependencies": { | ||
| "@google/clasp": "^2.3.0", | ||
| "@types/google-apps-script": "^1.0.17", | ||
| "typescript": "^4.0.5", | ||
| "eslint": "^7.32.0", | ||
| "eslint-config-airbnb-base": "^14.2.1", | ||
| "eslint-plugin-import": "^2.22.1" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /** | ||
| * Creates a JSON response for the web app | ||
| * @param {Object} data - The data to include in the JSON response | ||
| * @returns {GoogleAppsScript.Content.TextOutput} The JSON response | ||
| */ | ||
| function createJsonResponse(data) { | ||
| return ContentService.createTextOutput(JSON.stringify(data)).setMimeType( | ||
| ContentService.MimeType.JSON, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Main web app handler that returns sheet modification timestamps | ||
| * @param {GoogleAppsScript.Events.DoGet} e - The GET request event | ||
| * @param {Object} io - Dependency injection object for testing | ||
| * @param {Function} io.getFileById - Function to get file by ID (defaults to DriveApp.getFileById) | ||
| * @returns {GoogleAppsScript.Content.TextOutput} JSON response with timestamp or error | ||
| */ | ||
| function doGet(e, io = {}) { | ||
| const { | ||
| getFileById = DriveApp.getFileById, | ||
| } = io; | ||
|
|
||
| const { sheetId } = e.parameter; | ||
| if (!sheetId) { | ||
| return createJsonResponse({ error: 'sheetId parameter required' }); | ||
| } | ||
|
|
||
| try { | ||
| const file = getFileById(sheetId); | ||
| const modifiedTime = file.getLastUpdated().toISOString(); | ||
| return createJsonResponse({ modifiedTime }); | ||
| } catch (error) { | ||
| return createJsonResponse({ error: error.toString() }); | ||
| } | ||
| } | ||
|
|
||
| // Test functions for use with Google Apps Script debugger | ||
| /** | ||
| * Test function for doGet with various scenarios | ||
| */ | ||
| function _testDoGet() { | ||
| console.log('Testing doGet with missing sheetId...'); | ||
| const resultMissing = doGet({ parameter: {} }); | ||
| console.log('Result:', resultMissing.getContent()); | ||
|
|
||
| console.log('Testing doGet with invalid sheetId...'); | ||
| const resultInvalid = doGet({ parameter: { sheetId: 'invalid-id' } }); | ||
| console.log('Result:', resultInvalid.getContent()); | ||
|
|
||
| console.log('Testing doGet with mock getFileById...'); | ||
| const mockFile = { | ||
| getLastUpdated: () => new Date('2023-12-01T15:30:45.123Z'), | ||
| }; | ||
| const mockIo = { | ||
| getFileById: () => mockFile, | ||
| }; | ||
| const resultMock = doGet({ parameter: { sheetId: 'test-id' } }, mockIo); | ||
| console.log('Result:', resultMock.getContent()); | ||
|
|
||
| // Note: To test with a valid sheet ID, replace 'your-test-sheet-id' | ||
| // with an actual Google Sheets ID you have access to | ||
| console.log('Testing doGet with valid sheetId (uncomment to test)...'); | ||
| // const resultValid = doGet({ parameter: { sheetId: 'your-test-sheet-id' } }); | ||
| // console.log('Result:', resultValid.getContent()); | ||
| } | ||
|
|
||
| /** | ||
| * Test function for createJsonResponse | ||
| */ | ||
| function _testCreateJsonResponse() { | ||
| console.log('Testing createJsonResponse...'); | ||
| const testData = { test: 'value', timestamp: new Date().toISOString() }; | ||
| const response = createJsonResponse(testData); | ||
| console.log('Response content:', response.getContent()); | ||
| console.log('Response MIME type:', response.getMimeType()); | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any particular reason to do it globally rather than
devDependencies?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added note that clasp can be installed as devDependency if preferred, though global installation is common for deployment tools. Commit ee3ee2e