1. מבוא
המטרה של Codelab הזו היא להבין איך לכתוב פונקציה של Cloud Functions כדי להגיב להעלאת קובץ CSV ל-Cloud Storage, לקרוא את התוכן שלו ולהשתמש בו כדי לעדכן גיליון אלקטרוני ב-Google Sheets באמצעות API של Sheets.
אפשר לראות זאת כפעולה אוטומטית של 'ייבוא כקובץ CSV', באופן ידני אחר. בכל פעימה. כך תוכלו לנתח נתונים (שייתכן שצוות אחר מפיק) בגיליון אלקטרוני ברגע שהם זמינים.
כך נראה היישום :
2. הגדרה ודרישות
הגדרת סביבה בקצב עצמאי
- נכנסים למסוף Cloud ויוצרים פרויקט חדש או עושים שימוש חוזר בפרויקט קיים. (אם עדיין אין לכם חשבון Gmail או G Suite, עליכם ליצור חשבון).
חשוב לזכור את מזהה הפרויקט, שם ייחודי לכל הפרויקטים ב-Google Cloud (השם שלמעלה כבר תפוס ולא מתאים לכם, סליחה). בהמשך ב-Codelab הזה, היא תיקרא PROJECT_ID
.
- בשלב הבא צריך להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבים של Google Cloud.
מעבר ב-Codelab הזה לא אמור לעלות הרבה, אם בכלל. חשוב לבצע את כל ההוראות בקטע 'ניקוי' שמסביר איך להשבית משאבים כדי שלא תצברו חיובים מעבר למדריך הזה. משתמשים חדשים ב-Google Cloud זכאים להשתתף בתוכנית תקופת ניסיון בחינם בשווי 1,200 ש"ח.
3. יצירה והגדרה של גיליון אלקטרוני ב-Google Sheets והפעלה של ה-API
קודם כול, יוצרים מסמך חדש ב-Sheets (הגיליון הזה יכול להיות שייך לכל משתמש). אחרי שיוצרים את המרחב המשותף, חשוב לזכור את המזהה שלו. הוא ישמש כמשתנה סביבה לפונקציה שנכתוב :
ממסוף GCP, מפעילים את Google Sheets API בפרויקט החדש שיצרתם על ידי מעבר לקטע 'ממשקי API ושירותים'. ואז 'API Library' קטע :
בקטע "IAM & אדמין" בקטע 'חשבונות שירות', ורשמו את האימייל של חשבון השירות שמשמש כברירת המחדל של App Engine. הוא אמור להיות בפורמט your-project-id@appspot.gserviceaccount.com
. כמובן שאפשר גם ליצור חשבון שירות משלכם ייעודי לפעולה הזו.
לסיום, פשוט מעניקים הרשאות עריכה לחשבון השירות בגיליון האלקטרוני באמצעות הלחצן "Share" לחצן :
באמצעות ההגדרה הזו, עכשיו אנחנו יכולים לכתוב את הפונקציה של Cloud Functions ולהגדיר אותה לשימוש בחשבון השירות הזה. הוא יוכל לכתוב כאן במסמך הגיליון האלקטרוני שיצרנו עכשיו.
4. יצירה של קטגוריית אחסון
ניצור את הקטגוריה שהפונקציה של Cloud Functions תעקוב אחריה כדי לאתר קובצי CSV חדשים.
במסוף, משתמשים בתפריט השמאלי כדי לנווט אל 'אחסון'... :
... ויוצרים קטגוריה חדשה בשם csv2sheet-POSTFIX
(מחליפים את POSTFIX בערך ייחודי) כך שכל שאר ההגדרות מוגדרות לערכי ברירת המחדל שלהן :
5. יצירת הפונקציה של Cloud Functions
עכשיו אפשר ליצור פונקציה של Cloud Functions בשם csv2sheet
שמופעלת כשמעלים קבצים לקטגוריה ספציפית של Cloud Storage. הקוד ייכתב ב-Node.js 8 עם פונקציות אסינכרוניות באמצעות העורך המוטבע ישירות במסוף Cloud :
חשוב להגדיר את הטריגר כ-Cloud Storage וכדי לשנות את שם הקטגוריה לשם שיצרתם בשלב הקודם.
מעדכנים גם את נקודת הכניסה לפונקציה שאנחנו עומדים לכתוב אל csv2sheet
:
עכשיו משנים את גוף הפונקציה ל-:
- להשתמש בממשקי Cloud Storage וב-Sheets API
- לסמן את הפונקציה
csv2sheet
בתורasync
- לקבל את השדה
fileName
מהמטא-נתונים של האירוע ב-Cloud Storage ולמצוא שם לגיליון החדש שניצור :
const {google} = require("googleapis");
const {Storage} = require("@google-cloud/storage")
exports.csv2sheet = async (data, context) => {
var fileName = data.name;
// basic check that this is a *.csv file, etc...
if (!fileName.endsWith(".csv")) {
console.log("Not a .csv file, ignoring.");
return;
}
// define name of new sheet
const sheetName = fileName.slice(0, -4);
// TODO!
};
השימוש בasync
כאן נדרש כדי להשתמש בawait
, כפי שנראה עוד רגע.
שתי אפשרויות חשובות במהלך יצירת פונקציה זו כוללות (לוחצים על הקישור 'עוד' בתחתית המסך) :
- משתמשים בתפריט הנפתח כדי לבחור את חשבון השירות שצוין למעלה
- מגדירים משתנה סביבה בשם
SPREADSHEET_ID
שאמור להתאים למסמך הגיליון שיצרתם קודם :
בשלב האחרון של ההגדרה, הנה התוכן של package.json
עם ממשקי Cloud Storage ו-Google Sheet API כשני יחסי התלות שבהם נשתמש (באמצעות העורך המוטבע PACKAGE.JSON) של המסוף:
{
"name": "csv2sheet",
"version": "0.0.42",
"dependencies": {
"googleapis": "^51.0.0",
"@google-cloud/storage": "^5.0.1"
}
}
לאחר ההגדרה של הכול כפי שמתואר, אפשר ללחוץ על 'יצירה' ! אחרי דקה קצרה, הפונקציה אמורה ליצור ולפרוס אותה.
6. הגדרת אימות ו-Sheets API
לפני שכותבים קוד נוסף בפונקציה של Cloud באמצעות העורך המוטבע, אנחנו צריכים לחסום את האפשרות ליצור API של לקוח Google עם היקפי אחסון ו-Sheets מתאימים (חשוב לזכור, זה חלק מפונקציית async
).
בעורך הפונקציות של המסוף, לוחצים על 'EDIT'. ומוסיפים את הקוד הבא לגוף הפונקציה csv2sheet
:
// block on auth + getting the sheets API object
const auth = await google.auth.getClient({
scopes: [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/devstorage.read_only"
]
});
משם אפשר ליצור לקוח Sheets API :
const sheetsAPI = google.sheets({version: 'v4', auth});
7. שימוש ב-Sheets API כדי ליצור גיליון ריק
לקוח של Sheets API מאפשר לנו ליצור גיליון חדש פשוט במסמך. אבל לפני שנמשיך, הנה הערה קצרה לגבי אוצר המילים:
- גיליון אלקטרוני הוא המסמך עצמו, והמזהה שלו מוזכר למעלה (כפי שמתואר למעלה, בכתובת ה-URL של המסמך)
- גיליון הוא אחת מהכרטיסיות במסמך, ואפשר להפנות אליו לפי השם שלו (שם הכרטיסייה) או מזהה שנוצר במהלך יצירת הגיליון
כדי לעשות את זה:
function addEmptySheet(sheetsAPI, sheetName) {
return new Promise((resolve, reject) => {
const emptySheetParams = {
spreadsheetId: process.env.SPREADSHEET_ID,
resource: {
requests: [
{
addSheet: {
properties: {
title: sheetName,
index: 1,
gridProperties: {
rowCount: 2000,
columnCount: 26,
frozenRowCount: 1
}
}
}
}
]
}
};
sheetsAPI.spreadsheets.batchUpdate( emptySheetParams, function(err, response) {
if (err) {
reject("The Sheets API returned an error: " + err);
} else {
const sheetId = response.data.replies[0].addSheet.properties.sheetId;
console.log("Created empty sheet: " + sheetId);
resolve(sheetId);
}
}
);
});
}
שימו לב שבמקום לכתוב בתוך הקוד את ההפניה לגיליון האלקטרוני, אנחנו מסתמכים על משתנה הסביבה SPREADSHEET_ID
שנוצר בעבר.
אנחנו צריכים לזכור את הכתובת sheetId
כדי לשלוח בקשות נוספות לגיליון הספציפי הזה. כמו כן, שם הגיליון צריך להיות ייחודי, והיצירה תיכשל אם כבר קיים גיליון בשם sheetName
.
הפונקציה batchUpdate
ב-API של Sheets היא דרך נפוצה לבצע פעולות במסמכים, והיא מתוארת כאן.
8. קריאת נתונים מקובץ אחסון בפורמט CSV
עכשיו, אחרי שיש לנו מקום להציב בו את הנתונים, נמשיך לפתח את הפונקציה ב-Cloud Functions בעורך המוטבע ומשתמשים ב-Cloud Storage API כדי לשלוף את הנתונים עצמם מהקובץ שהועלה ולאחסן אותם במחרוזת:
function readCSVContent(sheetsAPI, file, sheetName) {
return new Promise((resolve, reject) => {
const storage = new Storage();
let fileContents = new Buffer('');
storage.bucket(file.bucket).file(file.name).createReadStream()
.on('error', function(err) {
reject('The Storage API returned an error: ' + err);
})
.on('data', function(chunk) {
fileContents = Buffer.concat([fileContents, chunk]);
})
.on('end', function() {
let content = fileContents.toString('utf8');
console.log("CSV content read as string : " + content );
resolve(content);
});
});
}
9. אכלוס הגיליון החדש שנוצר
עכשיו הגיע הזמן לאכלס את הגיליון שיצרנו באמצעות אותו API של הלקוח ב-Sheets והנתונים שאספנו עכשיו. אנחנו נשתמש בהזדמנות הזו כדי להוסיף עיצוב מסוים לעמודות הגיליון (שינוי גודל הגופן של השורה העליונה והפיכתו למודגש) :
function populateAndStyle(sheetsAPI, theData, sheetId) {
return new Promise((resolve, reject) => {
// Using 'batchUpdate' allows for multiple 'requests' to be sent in a single batch.
// Populate the sheet referenced by its ID with the data received (a CSV string)
// Style: set first row font size to 11 and to Bold. Exercise left for the reader: resize columns
const dataAndStyle = {
spreadsheetId: process.env.SPREADSHEET_ID,
resource: {
requests: [
{
pasteData: {
coordinate: {
sheetId: sheetId,
rowIndex: 0,
columnIndex: 0
},
data: theData,
delimiter: ","
}
},
{
repeatCell: {
range: {
sheetId: sheetId,
startRowIndex: 0,
endRowIndex: 1
},
cell: {
userEnteredFormat: {
textFormat: {
fontSize: 11,
bold: true
}
}
},
fields: "userEnteredFormat(textFormat)"
}
}
]
}
};
sheetsAPI.spreadsheets.batchUpdate(dataAndStyle, function(err, response) {
if (err) {
reject("The Sheets API returned an error: " + err);
} else {
console.log(sheetId + " sheet populated with " + theData.length + " rows and column style set.");
resolve();
}
});
});
}
צריך להוסיף את הקוד הזה לפונקציה של Cloud, שמשלימים עכשיו 99%!
שימו לב איך הנתונים והסגנון משולבים כמספר requests
בקריאה אחת ל-Sheets API batchUpdate
. כך אפשר לבצע עדכון אטומי יעיל יותר.
בנוסף, אנחנו מגדירים טווח עריכה שתואם לגודל הגיליון שיצרנו. המשמעות היא שתוכן שמכיל יותר מ-26 עמודות (הערך columnCount
ששימש ליצירת הגיליון) ייכשל עם הקוד הספציפי הזה.
אם הכול בסדר, בשלב הזה תוכלו:
- שומרים את הפונקציה המעודכנת
- לשחרר קובץ CSV בקטגוריה
- לראות את הנתונים המתאימים בחלון קופץ בגיליון האלקטרוני!
10. סיכום של כל המידע ובדיקת הזרימה
את הקריאות לפונקציות שעליהם דיברנו אפשר לבצע כקריאות חסימה עוקבות בפונקציה csv2sheet
המקורית:
const sheetId = await addEmptySheet(sheetsAPI, sheetName);
const theData = await readCSVContent(sheetsAPI, data, sheetName);
await populateAndStyle(sheetsAPI, theData, sheetId);
אם אתם צריכים את קוד המקור המלא של הפונקציה, הוא זמין כאן (כנראה קל יותר לקבל את כולו בקבוצה אחת).
אחרי שהכול מוכן, פשוט מעלים קובץ CSV לקטגוריה הנכונה ובודקים שהגיליון האלקטרוני מתעדכן בגיליון חדש עם תוכן הקובץ. אם אין לך קובץ כזה, צירפנו קובץ CSV לדוגמה.
נסו להעלות מספר קבצים לקטגוריה כדי לראות מה קורה.
11. זהו! הגיע הזמן להרוס את התשתית
בצחוק, אין תשתית שאפשר לפרק. הכול נעשה ללא שרת (serverless)!
אתם יכולים למחוק את הפונקציה של Cloud Functions ואת הקטגוריה שיצרתם, או אפילו את הפרויקט כולו.
12. מה השלב הבא?
הגענו לסוף של ה-Codelab הזה שמסביר את השלבים להאזנה להעלאות לקטגוריה של Cloud Storage בפונקציה של Cloud Functions, לצורך עדכון גיליון אלקטרוני ב-Google Sheets באמצעות ה-API המתאים.
הנה כמה פעולות שתוכלו לבצע :
- כדאי לעיין במדריכי השימוש של Cloud Functions (כולל כמה שיטות מומלצות)
- מעבר על אחד מהמדריכים של Cloud Functions
- מידע נוסף על Google Sheets API
אם נתקלתם בבעיות עם ה-Codelab הזה, אתם מוזמנים לדווח על כל בעיה באמצעות הקישור שבפינה הימנית התחתונה.
תודה על המשוב!