-
Notifications
You must be signed in to change notification settings - Fork 35
/
Code.gs
159 lines (143 loc) · 4.87 KB
/
Code.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// add a menu to the toolbar...
const createMenu = () => {
const menu = SpreadsheetApp.getUi()
.createMenu('Publish to S3')
.addItem('Configure...', 'showConfig');
if (hasRequiredProps()) {
menu.addItem('Publish', 'publish');
}
menu.addToUi();
};
// ...when the add-on is installed or opened
const onOpen = () => {
createMenu();
};
const onInstall = () => {
createMenu();
};
// https://github.com/liddiard/google-sheet-s3/issues/3#issuecomment-1276788590
const s3PutObject = (objectName, object) => {
const props = PropertiesService.getDocumentProperties().getProperties();
const contentType = 'application/json';
const contentBlob = Utilities.newBlob(JSON.stringify(object), contentType);
contentBlob.setName(objectName);
const service = 's3';
const region = props.awsRegion;
const action = 'PutObject';
const params = {};
const method = 'PUT';
const payload = contentBlob.getDataAsString();
const headers = {
'Content-Type': contentType
};
const uri = `/${objectName}`;
const options = {
Bucket: props.bucketName
};
AWS.init(props.awsAccessKeyId, props.awsSecretKey);
return AWS.request(service, region, action, params, method, payload, headers, uri, options);
};
// checks if document has the required configuration settings to publish to S3
// Note: does not check if the config is valid
const hasRequiredProps = () => {
const props = PropertiesService.getDocumentProperties().getProperties();
const requiredProps = [
'bucketName',
'awsRegion',
'awsAccessKeyId',
'awsSecretKey'
];
return requiredProps.every(prop => props[prop]);
};
// publish updated JSON to S3 if changes were made to the first sheet
const publish = () => {
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const props = PropertiesService.getDocumentProperties().getProperties();
// do nothing if required configuration settings are not present, or
// if the edited sheet is not the first one (sheets are indexed from 1,
// not 0)
if (!hasRequiredProps() || sheet.getActiveSheet().getIndex() > 1) {
return;
}
// get cell values from the range that contains data (2D array)
const rows = sheet
.getDataRange()
.getValues()
// filter out empty rows
.filter(row =>
row.some(val => typeof val !== 'string' || val.length)
)
// filter out columns that don't have a header (i.e. text in row 1)
.map((row, _, rows) =>
row.filter((_, index) => rows[0][index].length)
);
// create an array of cell objects keyed by header
const cells = rows
// exclude the header row
.slice(1)
.map(row =>
row.reduce((acc, val, index) =>
// represent blank cell values as `null`
// blank cells always appear as an empty string regardless of the data
// type of other values in the column. neutralizing everything to `null`
// lets us avoid mixing empty strings with other data types within a column.
Object.assign(
acc,
{ [rows[0][index]]: (typeof val === 'string' && !val.length) ? null : val }
)
, {})
);
// upload to AWS S3
const response = s3PutObject([props.path, sheet.getId()].join('/'), cells);
const error = response.toString(); // response is empty if publishing successful
if (error) {
throw error;
}
};
// show the configuration modal dialog UI
const showConfig = () => {
const sheet = SpreadsheetApp.getActiveSpreadsheet(),
props = PropertiesService.getDocumentProperties().getProperties(),
// default to empty strings, otherwise the string "undefined" will be shown
// for the value
defaultProps = {
bucketName: '',
path: '',
awsRegion: '',
awsAccessKeyId: '',
awsSecretKey: ''
}
template = HtmlService.createTemplateFromFile('config');
template.sheetId = sheet.getId();
Object.assign(template, defaultProps, props);
SpreadsheetApp.getUi()
.showModalDialog(template.evaluate(), 'Amazon S3 publishing configuration');
};
// update document configuration with values from the modal
const updateConfig = form => {
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const currentProps = PropertiesService.getDocumentProperties().getProperties();
PropertiesService.getDocumentProperties().setProperties({
...currentProps,
...form
});
let title, message;
if (hasRequiredProps()) {
try {
publish();
title = '✓ Configuration updated';
message = `Published spreadsheet will be accessible at:\nhttps://${form.bucketName}.s3.amazonaws.com/${form.path}/${sheet.getId()}`;
}
catch (error) {
title = '⚠ Error publishing to S3';
message = error;
}
}
else {
title = '⚠ Required info missing';
message = 'You need to fill out all highlighted fields for your spreadsheet to be published to S3.';
}
createMenu(); // update menu to show the "Publish" item if needed
const ui = SpreadsheetApp.getUi();
ui.alert(title, message, ui.ButtonSet.OK);
};