This repository has been archived by the owner on Oct 21, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 28
feat: add tiny GraphQL Lambda #42
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,4 +58,11 @@ typings/ | |
.env | ||
|
||
# next.js build output | ||
.next | ||
.next | ||
|
||
# Webpack | ||
.webpack | ||
|
||
# Serverless | ||
.serverless | ||
.webpack |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const mongoose = require("mongoose"); | ||
const bluebird = require("bluebird"); | ||
mongoose.Promise = bluebird; | ||
mongoose.Promise = global.Promise; | ||
|
||
// Only reconnect if needed. State is saved and outlives a handler invocation | ||
let isConnected; | ||
|
||
const connectToDatabase = () => { | ||
if (isConnected) { | ||
console.log("Re-using existing database connection"); | ||
return Promise.resolve(); | ||
} | ||
|
||
console.log("Creating new database connection"); | ||
return mongoose.connect(process.env.MONGODB_URL).then(db => { | ||
isConnected = db.connections[0].readyState; | ||
}); | ||
}; | ||
|
||
module.exports = connectToDatabase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { graphqlLambda, graphiqlLambda } from "apollo-server-lambda"; | ||
import lambdaPlayground from "graphql-playground-middleware-lambda"; | ||
import { makeExecutableSchema } from "graphql-tools"; | ||
import { mergeResolvers, mergeTypes } from "merge-graphql-schemas"; | ||
import { userType } from "./types/user"; | ||
import { userResolver } from "./resolvers/user"; | ||
|
||
const types = mergeTypes([userType]); | ||
const solvers = mergeResolvers([userResolver]); | ||
const graphqlSchema = makeExecutableSchema({ | ||
typeDefs: types, | ||
resolvers: solvers, | ||
logger: console | ||
}); | ||
|
||
// Database connection logic lives outside of the handler for performance reasons | ||
const connectToDatabase = require("./db"); | ||
|
||
const server = require("apollo-server-lambda"); | ||
|
||
exports.graphqlHandler = function graphqlHandler(event, context, callback) { | ||
/* Cause Lambda to freeze the process and save state data after | ||
the callback is called the effect is that new handler invocations | ||
will be able to re-use the database connection. | ||
See https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html | ||
and https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs */ | ||
context.callbackWaitsForEmptyEventLoop = false; | ||
|
||
function callbackFilter(error, output) { | ||
if (!output.headers) { | ||
output.headers = {}; | ||
} | ||
// eslint-disable-next-line no-param-reassign | ||
output.headers["Access-Control-Allow-Origin"] = "*"; | ||
output.headers["Access-Control-Allow-Credentials"] = true; | ||
output.headers["Content-Type"] = "application/json"; | ||
|
||
callback(error, output); | ||
} | ||
|
||
const handler = server.graphqlLambda({ schema: graphqlSchema }); | ||
|
||
connectToDatabase() | ||
.then(() => { | ||
return handler(event, context, callbackFilter); | ||
}) | ||
.catch(err => { | ||
console.log("MongoDB connection error: ", err); | ||
// TODO: return 500? | ||
process.exit(); | ||
}); | ||
}; | ||
|
||
exports.apiHandler = lambdaPlayground({ | ||
This comment was marked as off-topic.
Sorry, something went wrong. |
||
endpoint: process.env.GRAPHQL_ENDPOINT_URL | ||
? process.env.GRAPHQL_ENDPOINT_URL | ||
: "/production/graphql" | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
const mongoose = require("mongoose"); | ||
const validator = require("validator"); | ||
This comment was marked as off-topic.
Sorry, something went wrong. |
||
|
||
const Schema = mongoose.Schema; | ||
const SchemaTypes = Schema.Types; | ||
|
||
const userSchema = new Schema({ | ||
email: { | ||
type: "string" | ||
}, | ||
newEmail: { | ||
type: "string" | ||
}, | ||
emailVerifyTTL: { | ||
type: "date" | ||
}, | ||
emailVerified: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
emailAuthLinkTTL: { | ||
type: "date" | ||
}, | ||
password: { | ||
type: "string" | ||
}, | ||
progressTimestamps: { | ||
type: "array", | ||
default: [] | ||
}, | ||
isBanned: { | ||
type: "boolean", | ||
description: "User is banned from posting to camper news", | ||
default: false | ||
}, | ||
isCheater: { | ||
type: "boolean", | ||
description: | ||
"Users who are confirmed to have broken academic honesty policy are marked as cheaters", | ||
default: false | ||
}, | ||
isGithubCool: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
githubId: { | ||
type: "string" | ||
}, | ||
githubURL: { | ||
type: "string" | ||
}, | ||
githubEmail: { | ||
type: "string" | ||
}, | ||
joinedGithubOn: { | ||
type: "date" | ||
}, | ||
website: { | ||
type: "string" | ||
}, | ||
githubProfile: { | ||
type: "string" | ||
}, | ||
_csrf: { | ||
type: "string" | ||
}, | ||
isMigrationGrandfathered: { | ||
type: "boolean", | ||
default: false | ||
}, | ||
username: { | ||
type: "string" | ||
}, | ||
bio: { | ||
type: "string", | ||
default: "" | ||
}, | ||
about: { | ||
type: "string", | ||
default: "" | ||
}, | ||
name: { | ||
type: "string", | ||
default: "" | ||
}, | ||
gender: { | ||
type: "string", | ||
default: "" | ||
}, | ||
location: { | ||
type: "string", | ||
default: "" | ||
}, | ||
picture: { | ||
type: "string", | ||
default: "" | ||
}, | ||
linkedin: { | ||
type: "string" | ||
}, | ||
codepen: { | ||
type: "string" | ||
}, | ||
twitter: { | ||
type: "string" | ||
}, | ||
currentStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
longestStreak: { | ||
type: "number", | ||
default: 0 | ||
}, | ||
sendMonthlyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendNotificationEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
sendQuincyEmail: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
isLocked: { | ||
type: "boolean", | ||
description: | ||
"Campers profile does not show challenges/certificates to the public", | ||
default: false | ||
}, | ||
currentChallengeId: { | ||
type: "string", | ||
description: "The challenge last visited by the user", | ||
default: "" | ||
}, | ||
currentChallenge: { | ||
type: {}, | ||
description: "deprecated" | ||
}, | ||
isUniqMigrated: { | ||
type: "boolean", | ||
description: "Campers completedChallenges array is free of duplicates", | ||
default: false | ||
}, | ||
isHonest: { | ||
type: "boolean", | ||
description: "Camper has signed academic honesty policy", | ||
default: false | ||
}, | ||
isFrontEndCert: { | ||
type: "boolean", | ||
description: "Camper is front end certified", | ||
default: false | ||
}, | ||
isDataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified", | ||
default: false | ||
}, | ||
isBackEndCert: { | ||
type: "boolean", | ||
description: "Campers is back end certified", | ||
default: false | ||
}, | ||
isFullStackCert: { | ||
type: "boolean", | ||
description: "Campers is full stack certified", | ||
default: false | ||
}, | ||
isRespWebDesignCert: { | ||
type: "boolean", | ||
description: "Camper is responsive web design certified", | ||
default: false | ||
}, | ||
is2018DataVisCert: { | ||
type: "boolean", | ||
description: "Camper is data visualization certified (2018)", | ||
default: false | ||
}, | ||
isFrontEndLibsCert: { | ||
type: "boolean", | ||
description: "Camper is front end libraries certified", | ||
default: false | ||
}, | ||
isJsAlgoDataStructCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is javascript algorithms and data structures certified", | ||
default: false | ||
}, | ||
isApisMicroservicesCert: { | ||
type: "boolean", | ||
description: "Camper is apis and microservices certified", | ||
default: false | ||
}, | ||
isInfosecQaCert: { | ||
type: "boolean", | ||
description: | ||
"Camper is information security and quality assurance certified", | ||
default: false | ||
}, | ||
isChallengeMapMigrated: { | ||
type: "boolean", | ||
description: "Migrate completedChallenges array to challenge map", | ||
default: false | ||
}, | ||
challengeMap: { | ||
type: "object", | ||
description: "A map by ID of all the user completed challenges", | ||
default: {} | ||
}, | ||
completedChallenges: { | ||
type: [ | ||
{ | ||
completedDate: "number", | ||
lastUpdated: "number", | ||
numOfAttempts: "number", | ||
id: "string", | ||
name: "string", | ||
completedWith: "string", | ||
solution: "string", | ||
githubLink: "string", | ||
verified: "boolean", | ||
challengeType: { | ||
type: "number", | ||
default: 0 | ||
} | ||
} | ||
], | ||
default: [] | ||
}, | ||
portfolio: { | ||
type: "array", | ||
default: [] | ||
}, | ||
rand: { | ||
type: "number", | ||
index: true | ||
}, | ||
tshirtVote: { | ||
type: "number" | ||
}, | ||
timezone: { | ||
type: "string" | ||
}, | ||
theme: { | ||
type: "string", | ||
default: "default" | ||
}, | ||
languageTag: { | ||
type: "string", | ||
description: "An IETF language tag", | ||
default: "en" | ||
}, | ||
badges: { | ||
type: { | ||
coreTeam: { | ||
type: "array", | ||
default: [] | ||
} | ||
}, | ||
default: {} | ||
} | ||
}); | ||
|
||
module.exports = mongoose.model("User", userSchema, "user"); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as off-topic.
Sorry, something went wrong.