├── .gitignore ├── index.js ├── .babelrc ├── circle.yml ├── src ├── utils │ └── helpers.js ├── schema │ ├── index.js │ └── aws.js └── api.js ├── README.md ├── LICENSE ├── package.json └── .eslintrc /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | *.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | require('./src/api'); 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["babel-plugin-graphql-js"] 4 | } 5 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 0.12.4 4 | test: 5 | override: 6 | - npm run lint 7 | - npm test 8 | -------------------------------------------------------------------------------- /src/utils/helpers.js: -------------------------------------------------------------------------------- 1 | exports.removeUndefinedKeys = function (obj) { 2 | Object.keys(obj).forEach((key) => { 3 | if (typeof obj[key] === 'undefined') { 4 | delete obj[key]; 5 | } 6 | }); 7 | return obj; 8 | }; 9 | -------------------------------------------------------------------------------- /src/schema/index.js: -------------------------------------------------------------------------------- 1 | import { GraphQLSchema, GraphQLObjectType } from 'graphql'; 2 | import { aws } from './aws'; 3 | 4 | const schema = new GraphQLSchema({ 5 | mutation: aws.Mutations, 6 | query: aws.Queries 7 | }); 8 | 9 | export default schema; 10 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { graphql } from 'graphql'; 3 | import graphqlHTTP from 'express-graphql'; 4 | 5 | import schema from './schema'; 6 | 7 | const app = express(); 8 | app.use('/', graphqlHTTP({ schema: schema, graphiql: true })); 9 | 10 | const server = app.listen(3000, function () { 11 | const host = server.address().address; 12 | const port = server.address().port; 13 | console.log('Amazon GraphQL Server running.'); 14 | console.log('http://%s:%s', host, port); 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL AWS 2 | GraphQL is an experimental repository to enable GraphQL queries and mutations wrapping the Node.js Amazon AWS SDK. 3 | 4 | ### Getting Started 5 | 6 | To get started you will need to create a `.env` file in the root of the repository containing the following infromation: 7 | ``` 8 | export AWS_ACCESS_KEY_ID=[AWS_ACCESS_KEY_ID] 9 | export AWS_SECRET_ACCESS_KEY=[AWS_SECRET_ACCESS_KEY] 10 | export AWS_REGION=[AWS_REGION] 11 | ``` 12 | 1. Once created, `source .env` 13 | 2. Run `npm start` 14 | 15 | #### Supported API Features 16 | * TBC 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Red Badger Consulting Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-aws", 3 | "version": "0.0.1", 4 | "description": "Amazon AWS GraphQL API", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/jonsharratt/graphql-aws.git" 8 | }, 9 | "keywords": [ 10 | "GraphQL", 11 | "AWS" 12 | ], 13 | "bugs": { 14 | "url": "https://github.com/jonsharratt/graphql-aws/issues" 15 | }, 16 | "main": "index.js", 17 | "scripts": { 18 | "start": "node index.js", 19 | "lint": "eslint src", 20 | "test": "echo \"Error: no test specified\" && exit 0" 21 | }, 22 | "devDependencies": { 23 | "babel-core": "^6.1.21", 24 | "babel-preset-es2015": "^6.1.18", 25 | "babel-plugin-graphql-js": "0.0.4", 26 | "babel-eslint": "^3.1.18", 27 | "chai": "^3.4.1", 28 | "eslint": "^1.10.1", 29 | "mocha": "^2.3.4", 30 | "eslint": "^0.23.0", 31 | "eslint-plugin-react": "^2.5.2" 32 | }, 33 | "author": "Red Badger Consulting Ltd", 34 | "license": "MIT", 35 | "dependencies": { 36 | "aws-sdk": "^2.1.36", 37 | "express": "^4.13.3", 38 | "express-graphql": "^0.4.4", 39 | "bluebird": "^2.9.32", 40 | "graphql": "^0.4.13" 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/schema/aws.js: -------------------------------------------------------------------------------- 1 | import AWS from 'aws-sdk'; 2 | import Promise from 'bluebird'; 3 | import { 4 | GraphQLObjectType, 5 | GraphQLNonNull, 6 | GraphQLList, 7 | GraphQLString 8 | } from 'graphql'; 9 | 10 | const sns = new Promise.promisifyAll(new AWS.SNS()); 11 | const sqs = new Promise.promisifyAll(new AWS.SQS()); 12 | 13 | export const aws = graphql`${` 14 | type Topic { 15 | arn: String! 16 | } 17 | 18 | type QueueAttributes { 19 | DelaySeconds: String 20 | MaximumMessageSize: String 21 | MessageRetentionPeriod: String 22 | Policy: String 23 | ReceiveMessageWaitTimeSeconds: String 24 | VisibilityTimeout: String 25 | } 26 | type Queue { 27 | QueueUrl: String! 28 | Attributes: QueueAttributes 29 | } 30 | type Message { 31 | MessageId: String 32 | MD5OfMessageBody: String 33 | MD5OfMessageAttributes: String 34 | Body: String 35 | ReceiptHandle: String 36 | } 37 | 38 | type Queries { 39 | topics(nextToken: String): [Topic] 40 | 41 | listQueues(QueueNamePrefix: String!): [String] 42 | receiveMessage(QueueUrl: String!): [Message] 43 | } 44 | 45 | type Mutations { 46 | createTopic(name: String!): Topic 47 | deleteTopic(arn: String!): String 48 | 49 | sendMessage(QueueUrl: String!, MessageBody: String!): Message 50 | deleteMessage(QueueUrl: String!, ReceiptHandle: String!): String! 51 | deleteQueue(QueueUrl: String!): String! 52 | createQueue(QueueName: String!): Queue 53 | } 54 | `} 55 | 56 | ${{ 57 | Queue: { 58 | QueueUrl: { 59 | resolve: (root) => { return root.QueueUrl || root; } 60 | }, 61 | Attributes: { 62 | resolve: (root) => { 63 | const params = { QueueUrl: root.QueueUrl, AttributeNames: ['All'] }; 64 | return sqs.getQueueAttributesAsync(params).then(result => result.Attributes); 65 | } 66 | } 67 | }, 68 | Message: { 69 | MD5OfMessageBody: { 70 | resolve: (root) => { return root.MD5OfMessageBody || root.MD5OfBody; } 71 | } 72 | }, 73 | Topic: { 74 | description: 'Represents an Amazon SNS topic.', 75 | arn: { 76 | description: 'The topic arn.', 77 | resolve: (root) => { return root.TopicArn; } 78 | } 79 | }, 80 | 81 | Queries: { 82 | topics: { 83 | resolve: (root, {nextToken}) => { 84 | return sns.listTopicsAsync({ NextToken: nextToken }).then((result) => { 85 | return result.Topics; 86 | }); 87 | } 88 | }, 89 | listQueues: { 90 | resolve: (root, {QueueNamePrefix}) => { 91 | return sqs.listQueuesAsync(QueueNamePrefix).then(result => result.QueueUrls); 92 | } 93 | }, 94 | receiveMessage: { 95 | resolve: (obj, {QueueUrl}) => { 96 | const params = { QueueUrl: QueueUrl }; 97 | return sqs.receiveMessageAsync(params).then(result => result.Messages); 98 | } 99 | } 100 | }, 101 | Mutations: { 102 | createTopic: { 103 | resolve: (obj, {name}) => { 104 | return sns.createTopicAsync({ Name: name }); 105 | } 106 | }, 107 | deleteTopic: { 108 | resolve: (obj, {arn}) => { 109 | return sns.deleteTopicAsync({ TopicArn: arn }).then((result) => { 110 | return result.ResponseMetadata.RequestId; 111 | }); 112 | } 113 | }, 114 | sendMessage: { 115 | resolve: (obj, {QueueUrl, MessageBody}) => { 116 | const params = { QueueUrl: QueueUrl, MessageBody: MessageBody }; 117 | return sqs.sendMessageAsync(params); 118 | } 119 | }, 120 | deleteMessage: { 121 | resolve: (obj, {QueueUrl, ReceiptHandle}) => { 122 | const params = { QueueUrl: QueueUrl, ReceiptHandle: ReceiptHandle }; 123 | return sqs.deleteMessageAsync(params).then(result => result.ResponseMetadata.RequestId); 124 | } 125 | }, 126 | deleteQueue: { 127 | resolve: (obj, {QueueUrl}) => { 128 | const params = { QueueUrl: QueueUrl }; 129 | return sqs.deleteQueueAsync(params).then(result => result.ResponseMetadata.RequestId); 130 | } 131 | }, 132 | createQueue: { 133 | resolve: (obj, {QueueName, Attributes}) => { 134 | const params = { QueueName: QueueName }; 135 | if (Attributes) { 136 | Object.assign(params, { Attributes: helpers.removeUndefinedKeys(Attributes) }); 137 | } 138 | return sqs.createQueueAsync(params).then((result) => { return { QueueUrl: result.QueueUrl }; }); 139 | } 140 | } 141 | } 142 | }}`; 143 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | parser: "babel-eslint" 3 | arrowFunctions: true 4 | blockBindings: true 5 | classes: true 6 | defaultParams: true 7 | destructuring: true 8 | forOf: true 9 | generators: true 10 | modules: true 11 | objectLiteralComputedProperties: true 12 | objectLiteralShorthandMethods: true 13 | objectLiteralShorthandProperties: true 14 | spread: true 15 | templateStrings: true 16 | env: 17 | node: true 18 | es6: true 19 | rules: 20 | comma-dangle: 0 21 | no-cond-assign: 2 22 | no-console: 0 23 | no-constant-condition: 2 24 | no-control-regex: 0 25 | no-debugger: 0 26 | no-dupe-args: 2 27 | no-dupe-keys: 2 28 | no-duplicate-case: 2 29 | no-empty: 2 30 | no-empty-class: 2 31 | no-ex-assign: 2 32 | no-extra-boolean-cast: 2 33 | no-extra-parens: 0 34 | no-extra-semi: 2 35 | no-func-assign: 2 36 | no-inner-declarations: 37 | - 2 38 | - "functions" 39 | no-invalid-regexp: 2 40 | no-irregular-whitespace: 2 41 | no-negated-in-lhs: 2 42 | no-obj-calls: 2 43 | no-regex-spaces: 2 44 | no-reserved-keys: 0 45 | no-sparse-arrays: 2 46 | no-unreachable: 2 47 | use-isnan: 2 48 | valid-jsdoc: 0 49 | valid-typeof: 2 50 | block-scoped-var: 0 51 | complexity: 0 52 | consistent-return: 0 53 | curly: 54 | - 2 55 | - "all" 56 | default-case: 0 57 | dot-notation: 0 58 | eqeqeq: 2 59 | guard-for-in: 2 60 | no-alert: 2 61 | no-caller: 2 62 | no-div-regex: 2 63 | no-empty-label: 2 64 | no-eq-null: 0 65 | no-eval: 2 66 | no-extend-native: 2 67 | no-extra-bind: 2 68 | no-fallthrough: 2 69 | no-floating-decimal: 2 70 | no-implied-eval: 2 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 0 74 | no-loop-func: 0 75 | no-multi-spaces: 2 76 | no-multi-str: 2 77 | no-native-reassign: 0 78 | no-new: 2 79 | no-new-func: 0 80 | no-new-wrappers: 2 81 | no-octal: 2 82 | no-octal-escape: 2 83 | no-param-reassign: 2 84 | no-process-env: 0 85 | no-proto: 2 86 | no-redeclare: 2 87 | no-return-assign: 2 88 | no-script-url: 2 89 | no-self-compare: 0 90 | no-sequences: 2 91 | no-throw-literal: 2 92 | no-unused-expressions: 2 93 | no-void: 2 94 | no-warning-comments: 0 95 | no-with: 2 96 | radix: 2 97 | vars-on-top: 0 98 | wrap-iife: 2 99 | yoda: 100 | - 2 101 | - "never" 102 | - 103 | exceptRange: true 104 | strict: 0 105 | no-catch-shadow: 2 106 | no-delete-var: 2 107 | no-label-var: 2 108 | no-shadow: 2 109 | no-shadow-restricted-names: 2 110 | no-undef: 2 111 | no-undef-init: 2 112 | no-undefined: 0 113 | no-unused-vars: 114 | - 2 115 | - 116 | vars: "all" 117 | args: "after-used" 118 | no-use-before-define: 0 119 | handle-callback-err: 120 | - 2 121 | - "error" 122 | no-mixed-requires: 123 | - 2 124 | - true 125 | no-new-require: 2 126 | no-path-concat: 2 127 | no-process-exit: 0 128 | no-restricted-modules: 0 129 | no-sync: 2 130 | indent: 131 | - 2 132 | - 2 133 | - 134 | indentSwitchCase: true 135 | brace-style: 136 | - 2 137 | - "1tbs" 138 | - 139 | allowSingleLine: true 140 | camelcase: 141 | - 2 142 | - 143 | properties: "always" 144 | comma-spacing: 0 145 | comma-style: 146 | - 2 147 | - "last" 148 | consistent-this: 0 149 | eol-last: 2 150 | func-names: 0 151 | func-style: 0 152 | key-spacing: 153 | - 2 154 | - 155 | beforeColon: false 156 | afterColon: true 157 | max-nested-callbacks: 0 158 | new-cap: 0 159 | new-parens: 2 160 | newline-after-var: 0 161 | no-array-constructor: 2 162 | no-inline-comments: 0 163 | no-lonely-if: 2 164 | no-mixed-spaces-and-tabs: 2 165 | no-multiple-empty-lines: 0 166 | no-nested-ternary: 0 167 | no-new-object: 2 168 | no-spaced-func: 2 169 | no-ternary: 0 170 | no-trailing-spaces: 2 171 | no-underscore-dangle: 0 172 | no-wrap-func: 2 173 | one-var: 174 | - 2 175 | - "never" 176 | operator-assignment: 177 | - 2 178 | - "always" 179 | padded-blocks: 0 180 | quote-props: 181 | - 2 182 | - "as-needed" 183 | quotes: 184 | - 2 185 | - "single" 186 | semi: 187 | - 2 188 | - "always" 189 | semi-spacing: 190 | - 2 191 | - 192 | before: false 193 | after: true 194 | sort-vars: 0 195 | space-after-keywords: 196 | - 2 197 | - "always" 198 | space-before-blocks: 199 | - 2 200 | - "always" 201 | space-before-function-paren: 202 | - 2 203 | - 204 | anonymous: "always" 205 | named: "never" 206 | space-in-brackets: 0 207 | space-in-parens: 0 208 | space-infix-ops: 209 | - 2 210 | - 211 | int32Hint: false 212 | space-return-throw-case: 2 213 | space-unary-ops: 214 | - 2 215 | - 216 | words: true 217 | nonwords: false 218 | spaced-line-comment: 219 | - 2 220 | - "always" 221 | wrap-regex: 0 222 | no-var: 0 223 | max-len: 224 | - 2 225 | - 80 226 | - 4 227 | --------------------------------------------------------------------------------