Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"packages/br-cal",
"packages/br-script",
"packages/discover-dl",
"packages/lm-sync"
"packages/lm-sync",
"packages/makesheet"
],
"devDependencies": {
"@endo/eslint-plugin": "^0.5.1",
Expand Down
1 change: 0 additions & 1 deletion packages/discover-dl/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"type": "module",
"devDependencies": {},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this out of scope? drop it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted the out-of-scope changes to discover-dl/package.json. Commit ee3ee2e

"dependencies": {
"csv-parse": "^5.4.0",
"jstoxml": "^3.2.7"
Expand Down
2 changes: 2 additions & 0 deletions packages/makesheet/.claspignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.git/**
node_modules/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added missing newline to .claspignore. Commit ee3ee2e

37 changes: 37 additions & 0 deletions packages/makesheet/.eslintrc.json
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"]
}
]
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .eslintrc.json file already had a newline at the end. No changes needed.

89 changes: 89 additions & 0 deletions packages/makesheet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 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 if you haven't already:
```bash
npm install -g @google/clasp
Copy link
Owner

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?

Copy link
Author

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

```

2. Login to Google Apps Script:
```bash
yarn login
```

3. Create a new Google Apps Script project and note the script ID.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link to details

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added link to Google Apps Script project creation documentation. Commit ee3ee2e


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:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cite (link) docs for how to deploy it

Copy link
Author

Choose a reason for hiding this comment

The 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
```

## 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
- `yarn deploy` - Push and deploy as web app (requires DEPLOYMENT_ID env var)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget what's the difference between push and deploy. remind me?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added clarification: push uploads files to remote project, deploy creates a new web app version accessible via URL. Commit ee3ee2e

- `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.
11 changes: 11 additions & 0 deletions packages/makesheet/appsscript.json
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"
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline at end of file. my editor seems to do that, but I hate it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The appsscript.json file already had a newline at the end. No changes needed.

46 changes: 46 additions & 0 deletions packages/makesheet/example-usage.mk
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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added missing newline to example-usage.mk. Commit ee3ee2e

21 changes: 21 additions & 0 deletions packages/makesheet/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "makesheet",
"version": "0.1.0",
"description": "Google Sheets timestamp service for Make-based workflows",
"main": "sheetInfo.js",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop main:
this code isn't suitable to be imported from other packages

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the main field since this code is not suitable for import from other packages. Commit ee3ee2e

"author": "Dan Connolly",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a conventional way to ack copilot's contribution?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated author field to "Dan Connolly (with GitHub Copilot)" to acknowledge contribution. Commit ee3ee2e

"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 *.js",
"lint-fix": "eslint --fix *.js"
},
"devDependencies": {
"@google/clasp": "^2.3.0",
"@types/google-apps-script": "^1.0.17",
"typescript": "^4.0.5"
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package.json file already had a newline at the end. No changes needed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what you're smokin'. It does not end with a newline as of ee3ee2e

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed missing newlines in package.json, appsscript.json, and .eslintrc.json. Commit 6b5b8a8

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe it does:

finquick/packages/makesheet$ tail -c1 package.json | od -An -t x1
 0a

odd. both github and emacs show it without one.

Maybe I got confused about which file somehow.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, the file does have the newline. The tail -c1 | od output showing 0a confirms it. Sometimes different editors and GitHub's display can be inconsistent about showing trailing newlines.

45 changes: 45 additions & 0 deletions packages/makesheet/sheetInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
function createJsonResponse(data) {
return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(
ContentService.MimeType.JSON,
);
}

function doGet(e) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add JSDoc with types.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added JSDoc with types to all functions in sheetInfo.js. Commit ee3ee2e

const { sheetId } = e.parameter;
if (!sheetId) {
return createJsonResponse({ error: 'sheetId parameter required' });
}

try {
const file = DriveApp.getFileById(sheetId);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DriveApp.getFileById is ambient authority. Note the convention for that:

function doGet(e, io = {}) {
    const {
      getFileById = DriveApp.getFileById,
    } = io;
    ...
    getFileById(sheetId)

This lets the caller inject an implementation for testing, or interpose an attenuated version, etc.

Please add a CONTRIBUTING.md with a section on this.
excerpt from https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline as appropriate

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented ambient authority pattern with dependency injection and created CONTRIBUTING.md with OCap discipline guidelines. Commit ee3ee2e

const modifiedTime = file.getLastUpdated().toISOString();
return createJsonResponse({ modifiedTime });
} catch (error) {
return createJsonResponse({ error: error.toString() });
}
}

// Test functions for use with Google Apps Script debugger
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());

// 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());
}

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());
}