Skip to content
This repository was archived by the owner on Mar 17, 2022. It is now read-only.

Refactor and update #6

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ typings/

# next.js build output
.next

# IDEs
.idea
16 changes: 13 additions & 3 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#! /usr/bin/env node

const fs = require('fs')
const fs = require('fs');
const fetchSchema = require('../fetchSchema');
var program = require('commander');
const AirtableGraphQL = require('../index');
const program = require('commander');

program
.command('pull')
Expand All @@ -17,6 +18,15 @@ program
}).then(schema => {
fs.writeFileSync('./schema.json', JSON.stringify(schema, null, 2), 'utf-8');
})
})
});

program
.command('start')
.option('-s --schema [path]', 'Path of the file containing Airtable schema', './schema.json')
.option('-p --port [port]', 'Port for the adapter to listen on', '8765')
.action(function (cmd) {
const api = new AirtableGraphQL(process.env.AIRTABLE_API_KEY, {schemaPath: cmd.schema});
api.listen({port: cmd.port});
});

program.parse(process.argv);
14 changes: 3 additions & 11 deletions columns/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ module.exports = airtableGraphql => {
}

if (column.options.format === "currency") {
return { type: graphql.GraphQLString };
return { type: graphql.GraphQLFloat };
}

if (column.options.format === "percent") {
return { type: graphql.GraphQLString };
return { type: graphql.GraphQLInt };
}

if (column.options.format === "duration") {
return { type: graphql.GraphQLString };
return { type: graphql.GraphQLInt };
}

return { type: graphql.GraphQLInt };
Expand All @@ -25,14 +25,6 @@ module.exports = airtableGraphql => {
resolver: (column, api) => (obj) => {
let value = obj.fields[column.name];

if (column.options.format === "currency") {
value = `${column.options.symbol}${value}`;
}

if (column.options.format === "percent") {
value = `${value}%`;
}

return value;
}
});
Expand Down
68 changes: 67 additions & 1 deletion convertSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,68 @@ const {
} = require("graphql");
const sanitize = require("./sanitize");

module.exports = (airtableSchema, columnSupport) => {
function reformatSchema(airtableSchema) {
let tablesById = {};

airtableSchema.tables.map(table => {
tablesById[table.id] = table;
});

return {
tables: airtableSchema.tables.map(table => ({
name: table.name,
columns: table.columns.filter(column => !['lookup', 'formula', 'rollup'].includes(column.type)).map(column => {
let options = {};

if (column.type === "select") {
options = {
choices: Object.values(column.typeOptions.choices).map(c => {
return c.name;
})
};
}

if (column.type === 'foreignKey') {
let tableName;
if (column.foreignTable && column.foreignTable.name) {
tableName = column.foreignTable.name;
}
else {
tableName = tablesById[column.typeOptions.foreignTableId].name;
}
options = {
relationship: column.typeOptions.relationship,
table: tableName
}
}

if (column.type === 'multiSelect') {
options = {
choices: Object.values(column.typeOptions.choices).map(c => {
return c.name
})
}
}

if (column.type === 'number') {
options = {
format: column.typeOptions.format,
symbol: column.typeOptions.symbol
}
}

return {
name: column.name,
type: column.type,
options: options
}
})
}))
};
}

function convertSchema(airtableSchema, columnSupport) {

const TYPES = [];

const queryType = {
Expand Down Expand Up @@ -61,4 +122,9 @@ module.exports = (airtableSchema, columnSupport) => {
return new GraphQLSchema({
query: new GraphQLObjectType(queryType)
});
}

module.exports = {
reformatSchema,
convertSchema
};
74 changes: 25 additions & 49 deletions fetchSchema.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
const puppeteer = require("puppeteer");

function removeForeignTables(obj) {
console.log(typeof obj);
if (typeof obj === 'object') {
let result = {};
for (let key in obj) {
if (obj.hasOwnProperty(key) && key !== 'foreignTable') {
result[key] = removeForeignTables(obj[key]);
}
}
if (Array.isArray(obj)) {
{
let arrayResult = [];
for (let i = 0; i < obj.length; i++) {
arrayResult.push(result[String(i)]);
}
result = arrayResult;
}
}
return result;
} else {
return obj;
}
}

module.exports = async config => {
console.log("Fetching schema...");
const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']});
Expand All @@ -10,55 +34,7 @@ module.exports = async config => {
await page.keyboard.press("Enter");
await page.waitForNavigation();
const schema = await page.evaluate(() => {
return {
tables: application.tables.map(table => ({
name: table.name,
columns: table.columns.map(column => {
let options = {};

if (column.type === "select") {
options = {
choices: Object.values(column.typeOptions.choices).map(c => {
return c.name;
})
};
}

if (column.type === 'foreignKey') {
options = {
relationship: column.typeOptions.relationship,
table: column.foreignTable.name
}
}

if (column.type === 'multiSelect') {
options = {
choices: Object.values(column.typeOptions.choices).map(c => {
return c.name
})
}
}

if (column.type === 'number') {
options = {
format: column.typeOptions.format
}
}

if (column.type === 'number') {
options = {
format: column.typeOptions.format
}
}

return {
name: column.name,
type: column.type,
options: options
}
})
}))
};
return removeForeignTables(application);
});
await browser.close();
return {
Expand Down
12 changes: 7 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
const { printSchema } = require("graphql");
const { ApolloServer } = require("apollo-server");
const convertSchema = require("./convertSchema");
const { convertSchema, reformatSchema } = require("./convertSchema");
const createResolvers = require("./createResolvers");
const airtable = require("airtable");
const fs = require('fs')
const fs = require('fs');

class AirtableGraphQL {
constructor(apiKey, config = {}) {
this.columns = {};
airtable.configure({ apiKey });
const schema = JSON.parse(fs.readFileSync(config.schemaPath || "./schema.json", "utf8"));

var normalizedPath = require("path").join(__dirname, "columns");
const rawSchema = JSON.parse(fs.readFileSync(config.schemaPath || "./schema.json", "utf8"));
const schema = reformatSchema(rawSchema);

const normalizedPath = require("path").join(__dirname, "columns");
require("fs")
.readdirSync(normalizedPath)
.forEach(file => {
require("./columns/" + file)(this);
});

this.api = airtable.base(schema.id);
this.api = airtable.base(rawSchema.id);
this.schema = convertSchema(schema, this.columns);

this.resolvers = createResolvers(
Expand Down
10 changes: 4 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ $ airtable-graphql pull --email=[your_email] --password=[your_password] --base=[

This will create a `schema.json` file which describes all of your bases tables and columns.

Create a file called `index.js` and add the following.
Use the `airtable-graphql start` command to start the adapter

```js
const AirtableGraphQL = require("airtable-graphql");
api = new AirtableGraphQL("airtable_api_key");
api.listen();
```
$ AIRTABLE_API_KEY={{api_key}} airtable-graphql start -s schema.json -p 8765
```

Run `node index.js`
Open your browser to localhost:8765 to start writing GraphQL queries against your Airtable data.

That's it!