diff --git a/docz/docs/03-demos/11-static/01-lume.md b/docz/docs/03-demos/11-static/01-lume.md index 73e3297..a11e255 100644 --- a/docz/docs/03-demos/11-static/01-lume.md +++ b/docz/docs/03-demos/11-static/01-lume.md @@ -1,7 +1,7 @@ --- title: Lume pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index sidebar_custom_props: type: native --- diff --git a/docz/docs/03-demos/11-static/02-gatsbyjs.md b/docz/docs/03-demos/11-static/02-gatsbyjs.md index 3ae9f9d..e9eeeae 100644 --- a/docz/docs/03-demos/11-static/02-gatsbyjs.md +++ b/docz/docs/03-demos/11-static/02-gatsbyjs.md @@ -1,7 +1,7 @@ --- title: GatsbyJS pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index sidebar_custom_props: type: native --- diff --git a/docz/docs/03-demos/11-static/05-vitejs.md b/docz/docs/03-demos/11-static/05-vitejs.md index 9055fec..63c95b2 100644 --- a/docz/docs/03-demos/11-static/05-vitejs.md +++ b/docz/docs/03-demos/11-static/05-vitejs.md @@ -1,7 +1,7 @@ --- title: ViteJS pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index sidebar_custom_props: type: bundler --- diff --git a/docz/docs/03-demos/11-static/08-nextjs.md b/docz/docs/03-demos/11-static/08-nextjs.md index fb742d2..aeffb2a 100644 --- a/docz/docs/03-demos/11-static/08-nextjs.md +++ b/docz/docs/03-demos/11-static/08-nextjs.md @@ -1,7 +1,7 @@ --- title: NextJS pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index --- :::note diff --git a/docz/docs/03-demos/11-static/09-nuxtjs.md b/docz/docs/03-demos/11-static/09-nuxtjs.md index ff01e70..220dc14 100644 --- a/docz/docs/03-demos/11-static/09-nuxtjs.md +++ b/docz/docs/03-demos/11-static/09-nuxtjs.md @@ -1,7 +1,7 @@ --- title: NuxtJS pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index --- `@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation diff --git a/docz/docs/03-demos/11-static/index.md b/docz/docs/03-demos/11-static/index.md index 61e79b4..f6b499b 100644 --- a/docz/docs/03-demos/11-static/index.md +++ b/docz/docs/03-demos/11-static/index.md @@ -1,7 +1,7 @@ --- title: Content and Static Sites pagination_prev: demos/extensions/index -pagination_next: demos/gsheet +pagination_next: demos/cloudata/index --- import DocCardList from '@theme/DocCardList'; diff --git a/docz/docs/03-demos/25-gsheet.md b/docz/docs/03-demos/19-cloudata/01-gsheet.md similarity index 96% rename from docz/docs/03-demos/25-gsheet.md rename to docz/docs/03-demos/19-cloudata/01-gsheet.md index 4da556b..ac5c36f 100644 --- a/docz/docs/03-demos/25-gsheet.md +++ b/docz/docs/03-demos/19-cloudata/01-gsheet.md @@ -1,6 +1,7 @@ --- title: Google Sheets pagination_prev: demos/static/index +pagination_next: demos/cli --- import Tabs from '@theme/Tabs'; diff --git a/docz/docs/03-demos/19-cloudata/08-airtable.md b/docz/docs/03-demos/19-cloudata/08-airtable.md new file mode 100644 index 0000000..9e758a2 --- /dev/null +++ b/docz/docs/03-demos/19-cloudata/08-airtable.md @@ -0,0 +1,193 @@ +--- +title: Airtable +pagination_prev: demos/static/index +pagination_next: demos/cli +--- + +At the time of writing (2023 February 15), Airtable recommends Personal Access +Tokens for interacting with the official API. + +## NodeJS Integration + +The main module is `airtable` and can be installed with `npm`: + +```bash +npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz airtable +``` + +To obtain a reference to a table, code needs a [PAT](#personal-access-token), +the name of the workspace (typically starting with `app`), and the name of the +desired table (the Excel import typically supports the worksheet name): + +```js +const Airtable = require("airtable"), XLSX = require("xlsx"); +/* query all records in a table */ +const conn = new Airtable({apiKey: "PAT...", /* and other options ... */}); +const table = conn.base("app...").table("tablename..."); +``` + +### Exporting Data + +When querying data, a result set will be a simple array of Record objects. The +`fields` property is a simple JS object compatible with `json_to_sheet`: + +```js +/** Create SheetJS worksheet from Airtable table */ +async function airtable_to_worksheet(table) { + /* get all rows */ + const result = await table.select().all(); + + /* pull raw objects from the result */ + // highlight-next-line + const aoo = result.map(r => r.fields); + + /* create a worksheet */ + const worksheet = XLSX.utils.json_to_sheet(aoo); + return worksheet; +} +``` + +:::caution + +The results are not guaranteed to be sorted. The official API includes options +for sorting by fields. + +::: + +### Importing Data + +When inserting records, each object should be wrapped in a parent object with a +single `fields` property: + +```js +/** Append records from a SheetJS worksheet to Airtable table */ +async function airtable_load_worksheet(table, worksheet) { + /* suppose the field names */ + const aoo = XLSX.utils.sheet_to_json(worksheet); + + /* reshape to be compatible with Airtable API */ + // highlight-next-line + const airtable_rows = aoo.map(fields => ({ fields })); + + /* upload data */ + return await table.create(airtable_rows); +} +``` + +## Complete Example + +0) Create a free Airtable account. + +### Personal Access Token + +:::note + +In the past, Airtable offered API keys. They are slated to deprecate API keys +in January 2024. They recommend "Personal Access Tokens" for operations. + +::: + +API actions will require a PAT, which must be created through the developer hub: + +1) Click on account icon (topright area of the page) and select "Developer Hub". + +2) Click "Create Token". + +3) In the form, make the following selections: + +- Name: enter any name (for example, "SheetJS Test") +- Scopes: `data.records:read` and `data.records:write` (adding 2 scopes) +- Access: "All current and future bases in all current and future workspaces" + +The form will look like the screenshot below: + +![Airtable PAT Form](pathname:///airtable/pat.png) + +4) Click "Create Token" and you will see a popup. Copy the token and save it. + +### Workspace + +For the purposes of this demo, a sample workspace should be created: + +5) Download + +6) Create a project in Airtable using "Quickly upload". Select "Microsoft Excel" +and select the downloaded file from step 1. Click "Upload", then "Import". + +7) A workspace will be created. The name will be found in the URL. For example: + +``` +https://airtable.com/appblahblah/tblblahblah/blahblah +--------------------/^^^^^^^^^^^/ workspace name +``` + +the first part after the `.com` will be the workspace name. + +### Exporting Data + +8) Save the following to `read.js`: + +```js title="read.js" +const Airtable = require("airtable"), XLSX = require("xlsx"); +// highlight-start +/* replace the value with the personal access token */ +const apiKey = "pat..."; +/* replace the value with the workspace name */ +const base = "app..."; +// highlight-end +(async() => { + const conn = new Airtable({ apiKey }); + const table = conn.base(base).table("Sheet1"); + const result = await table.select().all(); + const aoo = result.map(r => r.fields); + const ws = XLSX.utils.json_to_sheet(aoo); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); + XLSX.writeFile(wb, "SheetJSAirtable.xlsb"); +})(); +``` + +9) Replace the values in the highlighted lines with the PAT and workspace name. + +10) Run `node read.js`. The script should write `SheetJSAirtable.xlsb`. The file +can be opened in Excel. + +### Importing Data + +11) Create a file `SheetJSAirpend.xlsx` with some new records in sheet `Sheet1`: + +![Records to append](pathname:///airtable/airpend.png) + +`npx xlsx-cli SheetJSAirpend.xlsx` should print the following data: + +```csv +Sheet1 +Name,Index +Someone Else,47 +``` + +12) Save the following to `write.js`: + +```js title="write.js" +const Airtable = require("airtable"), XLSX = require("xlsx"); +// highlight-start +/* replace the value with the personal access token */ +const apiKey = "pat..."; +/* replace the value with the workspace name */ +const base = "app..."; +// highlight-end +(async() => { + const conn = new Airtable({ apiKey }); + const table = conn.base(base).table("Sheet1"); + const wb = XLSX.readFile("SheetJSAirpend.xlsx"); + const ws = wb.Sheets["Sheet1"]; + const aoo = XLSX.utils.sheet_to_json(ws); + await table.create(aoo.map(fields => ({ fields }))); +})(); +``` + +13) Replace the values in the highlighted lines with the PAT and workspace name. + +14) Run `node write.js`. Open Airtable and verify the new row: + +![Final Result in Airtable](pathname:///airtable/post.png) \ No newline at end of file diff --git a/docz/docs/03-demos/19-cloudata/_category_.json b/docz/docs/03-demos/19-cloudata/_category_.json new file mode 100644 index 0000000..015a336 --- /dev/null +++ b/docz/docs/03-demos/19-cloudata/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Cloud Data Platforms", + "position": 19, + "collapsed": false +} \ No newline at end of file diff --git a/docz/docs/03-demos/19-cloudata/index.md b/docz/docs/03-demos/19-cloudata/index.md new file mode 100644 index 0000000..4b0cca3 --- /dev/null +++ b/docz/docs/03-demos/19-cloudata/index.md @@ -0,0 +1,20 @@ +--- +title: Cloud Data Platforms +pagination_prev: demos/static/index +pagination_next: demos/cli +--- + +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + +Cloud Data Platforms are popular storage media for structured data, typically +offering APIs for programmatic data ingress and egress. Demos: + + diff --git a/docz/docs/03-demos/27-cli.md b/docz/docs/03-demos/27-cli.md index 48daeed..abc6516 100644 --- a/docz/docs/03-demos/27-cli.md +++ b/docz/docs/03-demos/27-cli.md @@ -1,5 +1,6 @@ --- title: Command-Line Tools +pagination_prev: demos/cloudata/index pagination_next: demos/engines/index --- diff --git a/docz/docs/03-demos/index.md b/docz/docs/03-demos/index.md index 97628f3..857ca73 100644 --- a/docz/docs/03-demos/index.md +++ b/docz/docs/03-demos/index.md @@ -83,32 +83,46 @@ run in the web browser, demos will include interactive examples. - [`Dropbox`](/docs/demos/hosting/dropbox) - [`GitHub`](/docs/demos/hosting/github) +### Cloud Data Services + +- [`Google Sheets`](/docs/demos/cloudata/gsheet) +- [`Airtable`](/docs/demos/cloudata/airtable) + ### Platforms and Integrations - [`Command-Line Tools`](/docs/demos/cli) - [`NodeJS Server-Side Processing`](/docs/demos/server#nodejs) - [`Deno Server-Side Processing`](/docs/demos/server#deno) -- [`Google Sheets API`](/docs/demos/gsheet) - [`Headless Automation`](/docs/demos/headless) -- [`Other JavaScript Engines`](/docs/demos/engines) - [`Databases and Structured Data Stores`](/docs/demos/database) - [`NoSQL and Unstructured Data Stores`](/docs/demos/nosql) - [`Legacy Internet Explorer`](/docs/demos/frontend/legacy#internet-explorer) ### Bundlers and Tooling -- [`browserify`](/docs/demos/bundler#browserify) -- [`bun`](/docs/demos/bundler#bun) -- [`esbuild`](/docs/demos/bundler#esbuild) -- [`parcel`](/docs/demos/bundler#parcel) -- [`requirejs`](/docs/demos/bundler#requirejs) -- [`rollup`](/docs/demos/bundler#rollup) -- [`snowpack`](/docs/demos/bundler#snowpack) -- [`swc`](/docs/demos/bundler#swc) -- [`systemjs`](/docs/demos/bundler#systemjs) -- [`vite`](/docs/demos/bundler#vite) -- [`webpack`](/docs/demos/bundler#webpack) -- [`wmr`](/docs/demos/bundler#wmr) +- [`browserify`](/docs/demos/frontend/bundler#browserify) +- [`bun`](/docs/demos/frontend/bundler#bun) +- [`esbuild`](/docs/demos/frontend/bundler#esbuild) +- [`parcel`](/docs/demos/frontend/bundler#parcel) +- [`requirejs`](/docs/demos/frontend/bundler#requirejs) +- [`rollup`](/docs/demos/frontend/bundler#rollup) +- [`snowpack`](/docs/demos/frontend/bundler#snowpack) +- [`swc`](/docs/demos/frontend/bundler#swc) +- [`systemjs`](/docs/demos/frontend/bundler#systemjs) +- [`vite`](/docs/demos/frontend/bundler#vite) +- [`webpack`](/docs/demos/frontend/bundler#webpack) +- [`wmr`](/docs/demos/frontend/bundler#wmr) + +### Other Programming Languages + +- [`JavaScript Engines`](/docs/demos/engines) +- [`Duktape (C)`](/docs/demos/engines/duktape) +- [`JavaScriptCore (Swift)`](/docs/demos/engines/jsc) +- [`Rhino (Java)`](/docs/demos/engines/duktape) +- [`Goja (Go)`](/docs/demos/engines/duktape) +- [`ExecJS (Ruby)`](/docs/demos/engines/duktape) +- [`JavaScript::Engine (Perl)`](/docs/demos/engines/duktape) + :::note diff --git a/docz/docusaurus.config.js b/docz/docusaurus.config.js index 033be83..dbd328c 100644 --- a/docz/docusaurus.config.js +++ b/docz/docusaurus.config.js @@ -190,6 +190,9 @@ const config = { { from: '/docs/demos/extendscript', to: '/docs/demos/extensions/extendscript/' }, { from: '/docs/demos/excelapi', to: '/docs/demos/extensions/excelapi/' }, { from: '/docs/demos/chromium', to: '/docs/demos/extensions/chromium/' }, + /* cloudata */ + { from: '/docs/demos/gsheet', to: '/docs/demos/cloudata/gsheet/' }, + { from: '/docs/demos/airtable', to: '/docs/demos/cloudata/airtable/' }, ] }] ] diff --git a/docz/static/airtable/airpend.png b/docz/static/airtable/airpend.png new file mode 100644 index 0000000..19978a1 Binary files /dev/null and b/docz/static/airtable/airpend.png differ diff --git a/docz/static/airtable/pat.png b/docz/static/airtable/pat.png new file mode 100644 index 0000000..70d08e5 Binary files /dev/null and b/docz/static/airtable/pat.png differ diff --git a/docz/static/airtable/post.png b/docz/static/airtable/post.png new file mode 100644 index 0000000..89ca5fb Binary files /dev/null and b/docz/static/airtable/post.png differ