├── .circleci ├── config.yml └── deploy-docs.sh ├── .github └── issue_template.md ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cli ├── .gitignore ├── .yarnrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── packages │ ├── graphcool-cli-core │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── requests.js │ │ ├── src │ │ │ ├── BufferedConsole.ts │ │ │ ├── LocalInvoker.ts │ │ │ ├── commands │ │ │ │ ├── account │ │ │ │ │ └── index.ts │ │ │ │ ├── add-template │ │ │ │ │ ├── add-template.ts │ │ │ │ │ └── getbin.ts │ │ │ │ ├── auth │ │ │ │ │ └── index.ts │ │ │ │ ├── clusters │ │ │ │ │ └── index.ts │ │ │ │ ├── console │ │ │ │ │ └── index.ts │ │ │ │ ├── delete │ │ │ │ │ └── index.ts │ │ │ │ ├── deploy │ │ │ │ │ ├── Bundler │ │ │ │ │ │ ├── Bundler.ts │ │ │ │ │ │ ├── TypescriptBuilder.ts │ │ │ │ │ │ └── proxies │ │ │ │ │ │ │ ├── byline.js │ │ │ │ │ │ │ ├── dev.js │ │ │ │ │ │ │ └── lambda.js │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── index.test.ts.snap │ │ │ │ │ ├── emptyDefinition.ts │ │ │ │ │ ├── index.test.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── nocks │ │ │ │ │ │ ├── default_definition.ts │ │ │ │ │ │ └── local_instance.ts │ │ │ │ │ └── test-project │ │ │ │ │ │ ├── graphcool.yml │ │ │ │ │ │ ├── src │ │ │ │ │ │ ├── hello.graphql │ │ │ │ │ │ └── hello.js │ │ │ │ │ │ └── types.graphql │ │ │ │ ├── example-command │ │ │ │ │ └── index.ts │ │ │ │ ├── export │ │ │ │ │ ├── Exporter.describe.ts │ │ │ │ │ ├── Exporter.ts │ │ │ │ │ ├── download │ │ │ │ │ │ ├── lists │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ │ ├── nodes │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ │ └── relations │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ └── index.ts │ │ │ │ ├── import │ │ │ │ │ ├── Importer.describe.ts │ │ │ │ │ ├── Importer.ts │ │ │ │ │ ├── Validator.test.ts │ │ │ │ │ ├── Validator.ts │ │ │ │ │ ├── fixtures │ │ │ │ │ │ └── basic │ │ │ │ │ │ │ ├── import.zip │ │ │ │ │ │ │ ├── import │ │ │ │ │ │ │ ├── import.zip │ │ │ │ │ │ │ ├── lists │ │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ │ │ ├── nodes │ │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ │ │ ├── relations │ │ │ │ │ │ │ │ └── 000001.json │ │ │ │ │ │ │ └── state.json │ │ │ │ │ │ │ ├── makeData.ts │ │ │ │ │ │ │ └── types.graphql │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── normalizers │ │ │ │ │ │ └── Normalizer.ts │ │ │ │ │ └── types.ts │ │ │ │ ├── info │ │ │ │ │ └── index.ts │ │ │ │ ├── init │ │ │ │ │ ├── index.test.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── invoke │ │ │ │ │ └── local.ts │ │ │ │ ├── list │ │ │ │ │ └── index.ts │ │ │ │ ├── local │ │ │ │ │ ├── Docker.ts │ │ │ │ │ ├── docker │ │ │ │ │ │ ├── .envrc │ │ │ │ │ │ └── docker-compose.yml │ │ │ │ │ ├── eject.ts │ │ │ │ │ ├── nocks │ │ │ │ │ │ └── up.ts │ │ │ │ │ ├── ps.ts │ │ │ │ │ ├── pull.ts │ │ │ │ │ ├── restart.ts │ │ │ │ │ ├── stop.ts │ │ │ │ │ ├── up.test.ts │ │ │ │ │ └── up.ts │ │ │ │ ├── logs │ │ │ │ │ └── function.ts │ │ │ │ ├── playground │ │ │ │ │ └── index.ts │ │ │ │ ├── reset │ │ │ │ │ └── reset.ts │ │ │ │ └── root-token │ │ │ │ │ └── index.ts │ │ │ ├── errors │ │ │ │ ├── InvalidTargetError.ts │ │ │ │ └── ServiceDoesntExistError.ts │ │ │ ├── examples.ts │ │ │ ├── getConsoleOutput.ts │ │ │ ├── index.ts │ │ │ ├── test │ │ │ │ └── mock-requests.ts │ │ │ └── util.ts │ │ ├── test │ │ │ └── init.js │ │ ├── tsconfig.json │ │ ├── tslint.json │ │ └── yarn.lock │ ├── graphcool-cli-engine │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── requests.js │ │ ├── src │ │ │ ├── Auth.ts │ │ │ ├── CLI.test.ts │ │ │ ├── CLI.ts │ │ │ ├── Client │ │ │ │ ├── Client.ts │ │ │ │ └── ping.ts │ │ │ ├── Command.test.ts │ │ │ ├── Command.ts │ │ │ ├── Config.oldtest.ts │ │ │ ├── Config.ts │ │ │ ├── Dispatcher │ │ │ │ └── Dispatcher.ts │ │ │ ├── Environment.describer.ts │ │ │ ├── Environment.test.ts │ │ │ ├── Environment.ts │ │ │ ├── EnvironmentMigrator.test.ts │ │ │ ├── EnvironmentMigrator.ts │ │ │ ├── Flags │ │ │ │ ├── boolean.ts │ │ │ │ ├── index.ts │ │ │ │ ├── number.ts │ │ │ │ └── string.ts │ │ │ ├── Help.ts │ │ │ ├── NotFound.ts │ │ │ ├── Output │ │ │ │ ├── Prompter.ts │ │ │ │ ├── StreamOutput.ts │ │ │ │ ├── actions │ │ │ │ │ ├── ActionBase.ts │ │ │ │ │ ├── SimpleAction.ts │ │ │ │ │ ├── SpinnerAction.ts │ │ │ │ │ └── screen.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── migration.ts │ │ │ │ ├── spinners.ts │ │ │ │ ├── subtle.ts │ │ │ │ ├── table.ts │ │ │ │ └── util.ts │ │ │ ├── Parser.test.ts │ │ │ ├── Parser.ts │ │ │ ├── Plugin │ │ │ │ ├── BuiltInPlugins.ts │ │ │ │ ├── Cache.ts │ │ │ │ ├── CorePlugins.ts │ │ │ │ ├── Lock.ts │ │ │ │ ├── Manager.ts │ │ │ │ ├── Plugin.ts │ │ │ │ ├── PluginPath.ts │ │ │ │ ├── Plugins.ts │ │ │ │ └── rwlockfile.ts │ │ │ ├── ProjectDefinition │ │ │ │ ├── ProjectDefinition.ts │ │ │ │ ├── Variables.ts │ │ │ │ ├── builtin-modules.ts │ │ │ │ ├── fsToProject.ts │ │ │ │ ├── projectToFs.ts │ │ │ │ └── yaml.ts │ │ │ ├── StatusChecker.ts │ │ │ ├── Topic.ts │ │ │ ├── __snapshots__ │ │ │ │ ├── Command.test.ts.snap │ │ │ │ └── Environment.test.ts.snap │ │ │ ├── check.ts │ │ │ ├── commands │ │ │ │ ├── help.ts │ │ │ │ ├── index.ts │ │ │ │ └── version.ts │ │ │ ├── defaultDefinition.ts │ │ │ ├── errors │ │ │ │ ├── EnvDoesntExistError.ts │ │ │ │ ├── ExitError.ts │ │ │ │ ├── PromptMaskError.ts │ │ │ │ └── RequiredFlagError.ts │ │ │ ├── fs.ts │ │ │ ├── index.ts │ │ │ ├── mock.ts │ │ │ ├── test │ │ │ │ └── getTmpDir.ts │ │ │ ├── types │ │ │ │ ├── common.ts │ │ │ │ └── rc.ts │ │ │ └── util.ts │ │ ├── test │ │ │ ├── init.js │ │ │ └── test-project │ │ │ │ ├── graphcool.yml │ │ │ │ ├── src │ │ │ │ ├── hello.graphql │ │ │ │ └── hello.js │ │ │ │ └── types.graphql │ │ ├── tsconfig.json │ │ ├── tslint.json │ │ ├── typings.d.ts │ │ ├── typings │ │ │ └── global.d.ts │ │ └── yarn.lock │ └── graphcool-cli │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ ├── tslint.json │ │ └── yarn.lock ├── precommit.sh ├── test.sh └── yarn.lock ├── docs └── 0.x │ ├── 01-Quickstart │ ├── 01-Frontend │ │ ├── 01-React │ │ │ ├── 01-Apollo.md │ │ │ └── 02-Relay.md │ │ ├── 02-React-Native │ │ │ └── 01-Apollo.md │ │ ├── 03-Vue │ │ │ └── 01-Apollo.md │ │ ├── 04-Angular │ │ │ └── 01-Apollo.md │ │ ├── 05-iOS │ │ │ └── 01-Apollo.md │ │ └── 06-Android │ │ │ └── 01-Apollo.md │ └── 02-Backend │ │ ├── 01-TypeScript │ │ └── 01-TypeScript.md │ │ └── 02-Node │ │ └── 01-Node.md │ ├── 02-Concepts │ ├── 01-Overview │ │ ├── 01-The-Graphcool-Framework.md │ │ ├── 02-Platform-Overview.md │ │ ├── 03-GraphQL-Backend-Architecture.md │ │ ├── 04-Hosting.md │ │ └── index.md │ ├── 02-Database-&-API │ │ ├── 01-GraphQL-Primer.md │ │ ├── 02-Database-&-Data-Modelling.md │ │ ├── 03-API-Capabilities.md │ │ ├── 04-Migrations.md │ │ └── index.md │ ├── 03-Functions-&-Events │ │ ├── 01-Overview.md │ │ ├── 02-Hooks.md │ │ ├── 03-Resolvers.md │ │ ├── 04-Subscriptions.md │ │ └── index.md │ └── 04-Auth-&-Security │ │ ├── 01-Authentication.md │ │ ├── 02-Authorization-&-Data-Protection.md │ │ └── index.md │ ├── 03-Tutorials │ ├── 01-Functions │ │ ├── 01-Hook-Functions.md │ │ ├── 02-Subscription-Functions.md │ │ └── index.md │ └── 02-Auth │ │ ├── 01-Authorization-for-a-CMS.md │ │ ├── 02-Authentication-with-Facebook-for-React-&-Apollo.md │ │ ├── auth0-config.png │ │ ├── auth0-integration.png │ │ ├── auth0.png │ │ ├── create-post-permissions.png │ │ ├── create-user-permissions.png │ │ ├── digits-auth-provider.png │ │ ├── digits.png │ │ ├── gc-email.png │ │ ├── index.md │ │ ├── post-permissions.png │ │ ├── react-apollo-auth0-lock.png │ │ └── user-permissions.png │ ├── 04-Reference │ ├── 01-Service-Definition │ │ ├── 01-Overview.md │ │ ├── 02-graphcool.yml.md │ │ ├── 03-Deploying-Services.md │ │ ├── 04-Templates.md │ │ ├── 05-Legacy-Console-Projects.md │ │ └── index.md │ ├── 02-Graphcool-CLI │ │ ├── 01-Overview.md │ │ ├── 02-Commands.md │ │ ├── 03-graphcoolrc.md │ │ └── index.md │ ├── 03-Database │ │ ├── 01-Overview.md │ │ ├── 02-Data-Modelling.md │ │ ├── 03-Migrations.md │ │ └── index.md │ ├── 04-GraphQL-API │ │ ├── 01-Overview.md │ │ ├── 02-Query-API.md │ │ ├── 03-Mutation-API.md │ │ ├── 04-Subscription-API.md │ │ ├── 05-File-Management.md │ │ ├── 06-Error-Handling.md │ │ ├── graphql-variables-type-name.png │ │ └── index.md │ ├── 05-Functions │ │ ├── 01-Overview.md │ │ ├── 02-Runtime-Environment.md │ │ ├── 03-Hooks.md │ │ ├── 04-Subscriptions.md │ │ ├── 05-Resolvers.md │ │ ├── 06-Error-Handling.md │ │ └── index.md │ ├── 06-Auth │ │ ├── 01-Overview.md │ │ ├── 02-Authentication │ │ │ ├── 01-Overview.md │ │ │ ├── 02-Authentication-Tokens.md │ │ │ └── copy-pat.gif │ │ ├── 03-Authorization │ │ │ ├── 01-Overview.md │ │ │ ├── 02-Permission-Parameters.md │ │ │ ├── 03-Permission-Queries.md │ │ │ └── 04-Important-Notes.md │ │ └── index.md │ ├── 07-Deployment │ │ ├── 01-Overview.md │ │ ├── 02-Database-Access.md │ │ └── index.md │ ├── 08-Data-Import-&-Export │ │ ├── 01-Data-Import.md │ │ ├── 02-Data-Export.md │ │ ├── 03-Normalized-Data-Format.md │ │ └── index.md │ └── 09-Migrate-to-Prisma │ │ ├── 01-Overview.md │ │ ├── 02-Data-Modeling-&-GraphQL-API.md │ │ ├── 03-Authentication-&-Authorization.md │ │ ├── 04-Functions.md │ │ ├── 05-File-Handling.md │ │ ├── 06-Server-Hosting.md │ │ └── index.md │ └── 05-FAQ │ ├── 01-Basic.md │ ├── 02-API.md │ ├── 03-Database.md │ ├── 04-Auth.md │ ├── 05-Functions.md │ └── 06-Other.md ├── examples ├── .gitignore ├── 0.x │ ├── auth │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── package.json │ │ ├── src │ │ │ ├── authenticate.graphql │ │ │ ├── authenticate.js │ │ │ ├── loggedInUser.graphql │ │ │ ├── loggedInUser.js │ │ │ ├── signup.graphql │ │ │ └── signup.js │ │ ├── types.graphql │ │ └── yarn.lock │ ├── crud-api │ │ ├── README.md │ │ ├── graphcool.yml │ │ └── types.graphql │ ├── env-variables-in-functions │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── src │ │ │ ├── hello.graphql │ │ │ └── hello.js │ │ └── types.graphql │ ├── full-example │ │ ├── .gitignore │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── package.json │ │ ├── src │ │ │ ├── checkout │ │ │ │ ├── pay.graphql │ │ │ │ ├── pay.ts │ │ │ │ ├── setOrderItem.graphql │ │ │ │ └── setOrderItem.ts │ │ │ ├── email-password │ │ │ │ ├── authenticate.graphql │ │ │ │ ├── authenticate.ts │ │ │ │ ├── loggedInUser.graphql │ │ │ │ ├── loggedInUser.ts │ │ │ │ ├── signup.graphql │ │ │ │ └── signup.ts │ │ │ ├── permissions │ │ │ │ ├── models │ │ │ │ │ ├── Item.graphql │ │ │ │ │ ├── Order.graphql │ │ │ │ │ └── User.graphql │ │ │ │ └── relations │ │ │ │ │ ├── Items.graphql │ │ │ │ │ └── Orders.graphql │ │ │ └── scripts │ │ │ │ └── seed.js │ │ ├── types.graphql │ │ └── yarn.lock │ ├── graphcool-lib │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── package.json │ │ ├── src │ │ │ ├── checkPostCount.js │ │ │ ├── postRandomDogImage.graphql │ │ │ └── postRandomDogImage.js │ │ ├── types.graphql │ │ └── yarn.lock │ ├── rest-wrapper │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── package.json │ │ ├── src │ │ │ ├── allBreeds.graphql │ │ │ ├── allBreeds.js │ │ │ ├── allSubBreeds.graphql │ │ │ ├── allSubBreeds.js │ │ │ ├── breedImages.graphql │ │ │ ├── breedImages.js │ │ │ ├── randomBreedImage.graphql │ │ │ ├── randomBreedImage.js │ │ │ ├── randomDogImage.graphql │ │ │ ├── randomDogImage.js │ │ │ ├── randomSubBreedImage.graphql │ │ │ ├── randomSubBreedImage.js │ │ │ ├── subBreedImages.graphql │ │ │ └── subBreedImages.js │ │ └── types.graphql │ ├── subscriptions │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── package.json │ │ ├── src │ │ │ ├── createFirstArticle.graphql │ │ │ └── createFirstArticle.js │ │ ├── types.graphql │ │ └── yarn.lock │ └── yaml-variables │ │ ├── README.md │ │ ├── graphcool.yml │ │ ├── src │ │ ├── greeting.graphql │ │ ├── hello.js │ │ └── hey.js │ │ └── types.graphql └── README.md ├── package.json ├── server ├── .buildkite │ ├── empty-pipeline.yml │ ├── hooks │ │ └── pre-command │ ├── pipeline.sh │ └── pipeline.yml ├── .codacy.yml ├── .gitignore ├── .sbtopts ├── .scalafmt.conf ├── README.md ├── backend-api-fileupload │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ └── main │ │ ├── resources │ │ ├── application.conf │ │ ├── graphiql.html │ │ └── logback.xml │ │ └── scala │ │ └── Server.scala ├── backend-api-relay │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ └── main │ │ ├── resources │ │ ├── application.conf │ │ ├── graphiql.html │ │ └── logback.xml │ │ └── scala │ │ ├── RelayMain.scala │ │ └── cool │ │ └── graph │ │ └── relay │ │ ├── auth │ │ └── integrations │ │ │ └── SigninIntegration.scala │ │ └── schema │ │ ├── RelayOutputMapper.scala │ │ └── RelaySchemaBuilder.scala ├── backend-api-schema-manager │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ └── main │ │ ├── resources │ │ ├── application.conf │ │ └── logback.xml │ │ └── scala │ │ ├── SchemaManagerMain.scala │ │ └── cool │ │ └── graph │ │ └── schemamanager │ │ ├── SchemaManagerDependencies.scala │ │ └── SchemaManagerServer.scala ├── backend-api-simple-subscriptions │ ├── README.md │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ └── main │ │ ├── resources │ │ ├── application.conf │ │ └── logback.xml │ │ └── scala │ │ └── cool │ │ └── graph │ │ └── subscriptions │ │ ├── SubscriptionDependencies.scala │ │ ├── SubscriptionsMain.scala │ │ ├── helpers │ │ ├── Auth.scala │ │ └── ProjectHelper.scala │ │ ├── metrics │ │ └── SubscriptionMetrics.scala │ │ ├── protocol │ │ ├── Converters.scala │ │ ├── SubscriptionProtocol.scala │ │ ├── SubscriptionProtocolSerializers.scala │ │ ├── SubscriptionRequest.scala │ │ ├── SubscriptionSessionActor.scala │ │ ├── SubscriptionSessionActorV05.scala │ │ └── SubscriptionSessionManager.scala │ │ ├── resolving │ │ ├── DatabaseEvents.scala │ │ ├── MutationChannelUtil.scala │ │ ├── SubscriptionResolver.scala │ │ ├── SubscriptionsManager.scala │ │ ├── SubscriptionsManagerForModel.scala │ │ ├── SubscriptionsManagerForProject.scala │ │ └── VariablesParser.scala │ │ └── util │ │ └── PlayJson.scala ├── backend-api-simple │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ ├── main │ │ ├── resources │ │ │ ├── application.conf │ │ │ ├── graphiql.html │ │ │ └── logback.xml │ │ └── scala │ │ │ ├── SimpleMain.scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── client │ │ │ └── schema │ │ │ └── simple │ │ │ ├── SimplePermissionSchemaBuilder.scala │ │ │ └── SimpleSchemaBuilder.scala │ │ └── test │ │ └── scala │ │ └── cool │ │ └── graph │ │ └── auth2 │ │ └── Spec1.scala ├── backend-api-subscriptions-websocket │ ├── README.md │ ├── build.sbt │ └── src │ │ ├── main │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── websockets │ │ │ ├── WebsocketMain.scala │ │ │ ├── WebsocketServer.scala │ │ │ ├── WebsocketSession.scala │ │ │ ├── metrics │ │ │ └── SubscriptionWebsocketMetrics.scala │ │ │ ├── protocol │ │ │ └── Request.scala │ │ │ └── services │ │ │ └── WebsocketServices.scala │ │ └── test │ │ └── scala │ │ └── cool │ │ └── graph │ │ └── subscriptions │ │ └── websockets │ │ └── WebsocketSessionSpec.scala ├── backend-api-system │ ├── .sbtopts │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ └── main │ │ ├── resources │ │ ├── application.conf │ │ ├── graphiql.html │ │ └── logback.xml │ │ └── scala │ │ └── cool │ │ └── graph │ │ ├── InternalMutactionRunner.scala │ │ ├── InternalMutation.scala │ │ ├── TrustedInternalMutation.scala │ │ └── system │ │ ├── ActionSchemaResolver.scala │ │ ├── RequestPipelineSchemaResolver.scala │ │ ├── SchemaBuilderImpl.scala │ │ ├── SystemDependencies.scala │ │ ├── SystemMain.scala │ │ ├── SystemServer.scala │ │ ├── SystemUserContext.scala │ │ ├── authorization │ │ ├── SystemAuth.scala │ │ └── SystemAuth2.scala │ │ ├── database │ │ ├── DbToModelMapper.scala │ │ ├── Initializers.scala │ │ ├── ModelToDbMapper.scala │ │ ├── SystemFields.scala │ │ ├── client │ │ │ └── ClientDbQueriesImpl.scala │ │ ├── finder │ │ │ ├── CachedProjectResolverImpl.scala │ │ │ ├── LogsDataResolver.scala │ │ │ ├── ProjectDatabaseFinder.scala │ │ │ ├── ProjectFinder.scala │ │ │ ├── ProjectQueries.scala │ │ │ ├── ProjectResolver.scala │ │ │ ├── UncachedProjectResolver.scala │ │ │ └── client │ │ │ │ └── ClientResolver.scala │ │ ├── schema │ │ │ ├── InternalDatabaseSchema.scala │ │ │ └── LogDatabaseSchema.scala │ │ ├── seed │ │ │ └── InternalDatabaseSeedActions.scala │ │ └── tables │ │ │ ├── Action.scala │ │ │ ├── ActionHandlerWebhook.scala │ │ │ ├── ActionTriggerMutationModel.scala │ │ │ ├── ActionTriggerMutationRelation.scala │ │ │ ├── AlgoliaSyncQuery.scala │ │ │ ├── Client.scala │ │ │ ├── Enum.scala │ │ │ ├── FeatureToggle.scala │ │ │ ├── Field.scala │ │ │ ├── FieldConstraint.scala │ │ │ ├── Function.scala │ │ │ ├── Integration.scala │ │ │ ├── IntegrationAuth0.scala │ │ │ ├── IntegrationDigits.scala │ │ │ ├── Log.scala │ │ │ ├── MappedColumns.scala │ │ │ ├── Model.scala │ │ │ ├── ModelPermission.scala │ │ │ ├── ModelPermissionField.scala │ │ │ ├── MutationLog.scala │ │ │ ├── MutationLogMutaction.scala │ │ │ ├── PackageDefinition.scala │ │ │ ├── Permission.scala │ │ │ ├── Project.scala │ │ │ ├── ProjectDatabase.scala │ │ │ ├── Relation.scala │ │ │ ├── RelationFieldMirror.scala │ │ │ ├── RelationPermission.scala │ │ │ ├── RelayId.scala │ │ │ ├── RootToken.scala │ │ │ ├── SearchProviderAlgolia.scala │ │ │ ├── Seat.scala │ │ │ └── Tables.scala │ │ ├── externalServices │ │ ├── Auth0.scala │ │ └── Auth0Extend.scala │ │ ├── metrics │ │ └── SystemMetrics.scala │ │ ├── migration │ │ ├── Diff.scala │ │ ├── ModuleMigrator.scala │ │ ├── ModuleMigratorActions.scala │ │ ├── ProjectConfig.scala │ │ ├── dataSchema │ │ │ ├── DataSchemaAstExtensions.scala │ │ │ ├── RelationDiff.scala │ │ │ ├── SchemaActions.scala │ │ │ ├── SchemaDiff.scala │ │ │ ├── SchemaExport.scala │ │ │ ├── SchemaFileHeader.scala │ │ │ ├── SchemaMigrator.scala │ │ │ ├── SdlSchemaParser.scala │ │ │ ├── Utils.scala │ │ │ └── validation │ │ │ │ ├── DiffAwareSchemaValidator.scala │ │ │ │ ├── SchemaErrors.scala │ │ │ │ ├── SchemaSyntaxValidator.scala │ │ │ │ └── SchemaValidator.scala │ │ ├── functions │ │ │ └── FunctionDiff.scala │ │ ├── permissions │ │ │ └── PermissionsDiff.scala │ │ ├── project │ │ │ └── ClientInterchange.scala │ │ └── rootTokens │ │ │ └── RootTokenDiff.scala │ │ ├── mutactions │ │ ├── client │ │ │ ├── CopyModelTableData.scala │ │ │ ├── CopyRelationTableData.scala │ │ │ ├── CreateClientDatabaseForProject.scala │ │ │ ├── CreateColumn.scala │ │ │ ├── CreateModelTable.scala │ │ │ ├── CreateRelationFieldMirrorColumn.scala │ │ │ ├── CreateRelationTable.scala │ │ │ ├── DeleteAllDataItems.scala │ │ │ ├── DeleteAllRelations.scala │ │ │ ├── DeleteAllRelayIds.scala │ │ │ ├── DeleteClientDatabaseForProject.scala │ │ │ ├── DeleteColumn.scala │ │ │ ├── DeleteModelTable.scala │ │ │ ├── DeleteRelationFieldMirrorColumn.scala │ │ │ ├── DeleteRelationTable.scala │ │ │ ├── OverwriteAllRowsForColumn.scala │ │ │ ├── OverwriteInvalidEnumForColumnWithMigrationValue.scala │ │ │ ├── PopulateNullRowsForColumn.scala │ │ │ ├── PopulateRelationFieldMirrorColumn.scala │ │ │ ├── RenameTable.scala │ │ │ ├── SyncModelToAlgoliaViaRequest.scala │ │ │ ├── UpdateColumn.scala │ │ │ └── UpdateRelationFieldMirrorColumn.scala │ │ └── internal │ │ │ ├── BumpProjectRevision.scala │ │ │ ├── CreateAction.scala │ │ │ ├── CreateActionHandlerWebhook.scala │ │ │ ├── CreateActionTriggerMutationModel.scala │ │ │ ├── CreateAlgoliaSyncQuery.scala │ │ │ ├── CreateAuthProvider.scala │ │ │ ├── CreateClient.scala │ │ │ ├── CreateEnum.scala │ │ │ ├── CreateField.scala │ │ │ ├── CreateFieldConstraint.scala │ │ │ ├── CreateFunction.scala │ │ │ ├── CreateIntegration.scala │ │ │ ├── CreateModel.scala │ │ │ ├── CreateModelPermission.scala │ │ │ ├── CreateModelPermissionField.scala │ │ │ ├── CreateModelWithoutSystemFields.scala │ │ │ ├── CreateOrUpdateProjectDatabase.scala │ │ │ ├── CreatePackageDefinition.scala │ │ │ ├── CreateProject.scala │ │ │ ├── CreateRelation.scala │ │ │ ├── CreateRelationFieldMirror.scala │ │ │ ├── CreateRelationPermission.scala │ │ │ ├── CreateRootToken.scala │ │ │ ├── CreateSearchProviderAlgolia.scala │ │ │ ├── CreateSeat.scala │ │ │ ├── CreateSystemFieldIfNotExists.scala │ │ │ ├── DeleteAction.scala │ │ │ ├── DeleteActionHandlerWebhook.scala │ │ │ ├── DeleteActionTriggerMutationModel.scala │ │ │ ├── DeleteAlgoliaSyncQuery.scala │ │ │ ├── DeleteAuthProvider.scala │ │ │ ├── DeleteClient.scala │ │ │ ├── DeleteEnum.scala │ │ │ ├── DeleteField.scala │ │ │ ├── DeleteFieldConstraint.scala │ │ │ ├── DeleteFunction.scala │ │ │ ├── DeleteIntegration.scala │ │ │ ├── DeleteModel.scala │ │ │ ├── DeleteModelPermission.scala │ │ │ ├── DeleteModelPermissionField.scala │ │ │ ├── DeletePackageDefinition.scala │ │ │ ├── DeleteProject.scala │ │ │ ├── DeleteProjectDatabase.scala │ │ │ ├── DeleteRelation.scala │ │ │ ├── DeleteRelationFieldMirror.scala │ │ │ ├── DeleteRelationPermission.scala │ │ │ ├── DeleteRootToken.scala │ │ │ ├── DeleteSearchProviderAlgolia.scala │ │ │ ├── DeleteSeat.scala │ │ │ ├── EjectProject.scala │ │ │ ├── ExportData.scala │ │ │ ├── InvalidateSchema.scala │ │ │ ├── JoinPendingSeats.scala │ │ │ ├── ResetClientPassword.scala │ │ │ ├── SetFeatureToggle.scala │ │ │ ├── SystemMutactionNoop.scala │ │ │ ├── UpdateAction.scala │ │ │ ├── UpdateAlgoliaSyncQuery.scala │ │ │ ├── UpdateAuthProvider.scala │ │ │ ├── UpdateClient.scala │ │ │ ├── UpdateClientPassword.scala │ │ │ ├── UpdateCustomerInAuth0.scala │ │ │ ├── UpdateEnum.scala │ │ │ ├── UpdateField.scala │ │ │ ├── UpdateFieldConstraint.scala │ │ │ ├── UpdateFunction.scala │ │ │ ├── UpdateIntegration.scala │ │ │ ├── UpdateModel.scala │ │ │ ├── UpdateModelPermission.scala │ │ │ ├── UpdateProject.scala │ │ │ ├── UpdateRelation.scala │ │ │ ├── UpdateRelationPermission.scala │ │ │ ├── UpdateSearchProviderAlgolia.scala │ │ │ ├── UpdateTypeAndFieldPositions.scala │ │ │ └── validations │ │ │ ├── EnumValueValidation.scala │ │ │ ├── MigrationAndDefaultValueValidation.scala │ │ │ ├── MutactionVerificationUtil.scala │ │ │ ├── ProjectValidations.scala │ │ │ ├── TypeNameValidation.scala │ │ │ └── URLValidation.scala │ │ ├── mutations │ │ ├── AddActionMutation.scala │ │ ├── AddAlgoliaSyncQueryMutation.scala │ │ ├── AddEnumMutation.scala │ │ ├── AddFieldConstraint.scala │ │ ├── AddFieldMutation.scala │ │ ├── AddModelMutation.scala │ │ ├── AddModelPermissionMutation.scala │ │ ├── AddProjectMutation.scala │ │ ├── AddRelationFieldMirrorMutation.scala │ │ ├── AddRelationMutation.scala │ │ ├── AddRelationPermissionMutation.scala │ │ ├── AddRequestPipelineMutationFunctionMutation.scala │ │ ├── AddSchemaExtensionFunctionMutation.scala │ │ ├── AddServerSideSubscriptionFunctionMutation.scala │ │ ├── AuthenticateCustomerMutation.scala │ │ ├── CloneProjectMutation.scala │ │ ├── CreateRootTokenMutation.scala │ │ ├── DefaultProjectDatabase.scala │ │ ├── DeleteActionMutation.scala │ │ ├── DeleteAlgoliaSyncQueryMutation.scala │ │ ├── DeleteCustomer.scala │ │ ├── DeleteEnumMutation.scala │ │ ├── DeleteFieldConstraintMutation.scala │ │ ├── DeleteFieldMutation.scala │ │ ├── DeleteFunctionMutation.scala │ │ ├── DeleteModelMutation.scala │ │ ├── DeleteModelPermissionMutation.scala │ │ ├── DeleteProjectMutation.scala │ │ ├── DeleteRelationFieldMirrorMutation.scala │ │ ├── DeleteRelationMutation.scala │ │ ├── DeleteRelationPermissionMutation.scala │ │ ├── DeleteRootTokenMutation.scala │ │ ├── EjectProjectMutation.scala │ │ ├── EnableAuthProviderMutation.scala │ │ ├── ExportDataMutation.scala │ │ ├── GenerateUserToken.scala │ │ ├── GetTemporaryUploadUrlMutation.scala │ │ ├── InstallPackageMutation.scala │ │ ├── InviteCollaboratorMutation.scala │ │ ├── MigrateEnumValuesMutation.scala │ │ ├── MigrateSchemaMutation.scala │ │ ├── MutationInput.scala │ │ ├── PushMutation.scala │ │ ├── RemoveCollaboratorMutation.scala │ │ ├── ResetClientPasswordMutation.scala │ │ ├── ResetProjectDataMutation.scala │ │ ├── ResetProjectSchemaMutation.scala │ │ ├── SetFeatureToggleMutation.scala │ │ ├── SetProjectDatabaseMutation.scala │ │ ├── SigninClientUserMutation.scala │ │ ├── SignupCustomerMutation.scala │ │ ├── TransferOwnershipMutation.scala │ │ ├── UninstallPackageMutation.scala │ │ ├── UpdateActionMutation.scala │ │ ├── UpdateAlgoliaSyncQueryMutation.scala │ │ ├── UpdateClientPasswordMutation.scala │ │ ├── UpdateCustomerMutation.scala │ │ ├── UpdateEnumMutation.scala │ │ ├── UpdateFieldConstraintMutation.scala │ │ ├── UpdateFieldMutation.scala │ │ ├── UpdateModelMutation.scala │ │ ├── UpdateModelPermissionMutation.scala │ │ ├── UpdateProjectMutation.scala │ │ ├── UpdateRelationMutation.scala │ │ ├── UpdateRelationPermissionMutation.scala │ │ ├── UpdateRequestPipelineMutationFunctionMutation.scala │ │ ├── UpdateSchemaExtensionFunctionMutation.scala │ │ ├── UpdateSearchProviderAlgoliaMutation.scala │ │ └── UpdateServerSideSubscriptionFunctionMutation.scala │ │ └── schema │ │ ├── fields │ │ ├── AddAction.scala │ │ ├── AddAlgoliaSyncQuery.scala │ │ ├── AddEnum.scala │ │ ├── AddField.scala │ │ ├── AddFieldConstraint.scala │ │ ├── AddModel.scala │ │ ├── AddModelPermission.scala │ │ ├── AddProject.scala │ │ ├── AddRelation.scala │ │ ├── AddRelationFieldMirror.scala │ │ ├── AddRelationPermission.scala │ │ ├── AddRequestPipelineMutationFunction.scala │ │ ├── AddSchemaExtensionFunction.scala │ │ ├── AddServerSideSubscriptionFunction.scala │ │ ├── AuthenticateCustomer.scala │ │ ├── CloneProjectQuery.scala │ │ ├── CreateRootToken.scala │ │ ├── DeleteAction.scala │ │ ├── DeleteAlgoliaSyncQuery.scala │ │ ├── DeleteCustomer.scala │ │ ├── DeleteEnum.scala │ │ ├── DeleteField.scala │ │ ├── DeleteFieldConstraint.scala │ │ ├── DeleteFunction.scala │ │ ├── DeleteModel.scala │ │ ├── DeleteModelPermission.scala │ │ ├── DeleteProject.scala │ │ ├── DeleteRelation.scala │ │ ├── DeleteRelationFieldMirror.scala │ │ ├── DeleteRelationPermission.scala │ │ ├── DeleteRootToken.scala │ │ ├── EjectProject.scala │ │ ├── EnableAuthProvider.scala │ │ ├── ExportData.scala │ │ ├── GenerateUserToken.scala │ │ ├── GetTemporaryDeploymentUrl.scala │ │ ├── InstallPackage.scala │ │ ├── InviteCollaborator.scala │ │ ├── MigrateSchema.scala │ │ ├── Push.scala │ │ ├── RemoveCollaborator.scala │ │ ├── ResetClientPassword.scala │ │ ├── ResetProjectData.scala │ │ ├── ResetProjectSchema.scala │ │ ├── SetFeatureToggle.scala │ │ ├── SetProjectDatabase.scala │ │ ├── SigninClientUser.scala │ │ ├── TransferOwnership.scala │ │ ├── TrustedMutation.scala │ │ ├── UninstallPackage.scala │ │ ├── UpdateAction.scala │ │ ├── UpdateAlgoliaSyncQuery.scala │ │ ├── UpdateClient.scala │ │ ├── UpdateClientPassword.scala │ │ ├── UpdateEnum.scala │ │ ├── UpdateField.scala │ │ ├── UpdateFieldConstraint.scala │ │ ├── UpdateModel.scala │ │ ├── UpdateModelPermission.scala │ │ ├── UpdateProject.scala │ │ ├── UpdateRelation.scala │ │ ├── UpdateRelationPermission.scala │ │ ├── UpdateRequestPipelineMutationFunction.scala │ │ ├── UpdateSchemaExtensionFunction.scala │ │ ├── UpdateSearchProviderAlgolia.scala │ │ └── UpdateServerSideSubscriptionFunction.scala │ │ └── types │ │ ├── Action.scala │ │ ├── ActionHandlerWebhook.scala │ │ ├── ActionTriggerMutationModel.scala │ │ ├── ActionTriggerMutationRelation.scala │ │ ├── AlgoliaSyncQuery.scala │ │ ├── AuthProvider.scala │ │ ├── Customer.scala │ │ ├── CustomerSource.scala │ │ ├── Enum.scala │ │ ├── FeatureToggle.scala │ │ ├── Field.scala │ │ ├── FieldConstraint.scala │ │ ├── FieldConstraintTypeType.scala │ │ ├── Function.scala │ │ ├── FunctionBinding.scala │ │ ├── FunctionType.scala │ │ ├── HistogramPeriod.scala │ │ ├── Integration.scala │ │ ├── IntegrationNameType.scala │ │ ├── IntegrationTypeType.scala │ │ ├── Log.scala │ │ ├── LogStatus.scala │ │ ├── Model.scala │ │ ├── ModelPermission.scala │ │ ├── Operation.scala │ │ ├── PackageDefinition.scala │ │ ├── PermissionQueryArgument.scala │ │ ├── PermissionQueryArguments.scala │ │ ├── Project.scala │ │ ├── ProjectDatabase.scala │ │ ├── Region.scala │ │ ├── Relation.scala │ │ ├── RelationFieldMirror.scala │ │ ├── RelationPermission.scala │ │ ├── Rule.scala │ │ ├── SchemaErrorType.scala │ │ ├── SearchProviderAlgolia.scala │ │ ├── Seat.scala │ │ ├── SeatStatus.scala │ │ ├── UserType.scala │ │ ├── VerbalDescription.scala │ │ ├── Viewer.scala │ │ ├── package.scala │ │ └── rootToken.scala ├── backend-shared │ ├── build.sbt │ ├── project │ │ ├── build.properties │ │ └── plugins.sbt │ └── src │ │ ├── main │ │ ├── resources │ │ │ ├── application.conf │ │ │ └── logback.xml │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ ├── FieldMetrics.scala │ │ │ ├── FilteredResolver.scala │ │ │ ├── GCDataTypes │ │ │ └── GCValues.scala │ │ │ ├── Mutaction.scala │ │ │ ├── RequestContext.scala │ │ │ ├── TransactionMutaction.scala │ │ │ ├── Types.scala │ │ │ ├── Utils.scala │ │ │ ├── client │ │ │ ├── Metrics.scala │ │ │ ├── MutationQueryWhitelist.scala │ │ │ ├── SangriaQueryArguments.scala │ │ │ ├── SchemaBuilderUtils.scala │ │ │ ├── UserContext.scala │ │ │ ├── database │ │ │ │ ├── DataResolver.scala │ │ │ │ ├── DatabaseMutationBuilder.scala │ │ │ │ ├── DatabaseQueryBuilder.scala │ │ │ │ ├── DeferredTypes.scala │ │ │ │ ├── FilterArguments.scala │ │ │ │ ├── IdBasedConnection.scala │ │ │ │ ├── ProjectDataresolver.scala │ │ │ │ ├── ProjectRelayIdTable.scala │ │ │ │ ├── QueryArguments.scala │ │ │ │ └── SlickExtensions.scala │ │ │ └── schema │ │ │ │ ├── ModelMutationType.scala │ │ │ │ ├── OutputMapper.scala │ │ │ │ ├── SchemaBuilderConstants.scala │ │ │ │ ├── SchemaModelObjectTypesBuilder.scala │ │ │ │ └── simple │ │ │ │ ├── SimpleOutputMapper.scala │ │ │ │ ├── SimplePermissionModelObjectTypesBuilder.scala │ │ │ │ └── SimpleSchemaModelObjectTypeBuilder.scala │ │ │ ├── deprecated │ │ │ ├── Action.scala │ │ │ ├── actions │ │ │ │ ├── MutationCallbackEvent.scala │ │ │ │ └── schemas │ │ │ │ │ ├── ActionUserContext.scala │ │ │ │ │ ├── CreateSchema.scala │ │ │ │ │ ├── DeleteSchema.scala │ │ │ │ │ ├── MutationMetaData.scala │ │ │ │ │ └── UpdateSchema.scala │ │ │ └── packageMocks │ │ │ │ ├── FacebookAuthProvider.scala │ │ │ │ ├── PackageMock.scala │ │ │ │ └── PackageParser.scala │ │ │ ├── shared │ │ │ ├── ApiMatrix.scala │ │ │ ├── BackendSharedMetrics.scala │ │ │ ├── DatabaseConstraints.scala │ │ │ ├── RelationFieldMirrorColumn.scala │ │ │ ├── SchemaSerializer.scala │ │ │ ├── TypeInfo.scala │ │ │ ├── adapters │ │ │ │ └── HttpFunctionHeaders.scala │ │ │ ├── algolia │ │ │ │ ├── AlgoliaContext.scala │ │ │ │ ├── Types.scala │ │ │ │ └── schemas │ │ │ │ │ ├── AlgoliaFullModelSchema.scala │ │ │ │ │ └── AlgoliaSchema.scala │ │ │ ├── authorization │ │ │ │ └── SharedAuth.scala │ │ │ ├── database │ │ │ │ └── GlobalDatabaseManager.scala │ │ │ ├── errors │ │ │ │ └── Errors.scala │ │ │ ├── externalServices │ │ │ │ ├── KinesisPublisher.scala │ │ │ │ ├── SnsPublisher.scala │ │ │ │ └── TestableTime.scala │ │ │ ├── functions │ │ │ │ ├── EndpointResolver.scala │ │ │ │ ├── FunctionEnvironment.scala │ │ │ │ ├── dev │ │ │ │ │ ├── DevFunctionEnvironment.scala │ │ │ │ │ └── Protocol.scala │ │ │ │ └── lambda │ │ │ │ │ ├── LambdaDeploymentAccount.scala │ │ │ │ │ └── LambdaFunctionEnvironment.scala │ │ │ ├── logging │ │ │ │ ├── LogData.scala │ │ │ │ └── RequestLogger.scala │ │ │ ├── models │ │ │ │ ├── Function.scala │ │ │ │ ├── ManagedFields.scala │ │ │ │ ├── ModelParser.scala │ │ │ │ └── Models.scala │ │ │ ├── mutactions │ │ │ │ ├── InvalidInput.scala │ │ │ │ └── MutationTypes.scala │ │ │ ├── queryPermissions │ │ │ │ └── PermissionSchemaResolver.scala │ │ │ └── schema │ │ │ │ ├── CustomScalarTypes.scala │ │ │ │ └── JsonMarshalling.scala │ │ │ ├── subscriptions │ │ │ ├── SubscriptionUserContext.scala │ │ │ └── schemas │ │ │ │ ├── MyVisitor.scala │ │ │ │ ├── QueryTransformer.scala │ │ │ │ ├── SubscriptionDataResolver.scala │ │ │ │ ├── SubscriptionQueryValidator.scala │ │ │ │ └── SubscriptionSchema.scala │ │ │ └── util │ │ │ ├── ErrorHandlerFactory.scala │ │ │ ├── collection │ │ │ └── ToImmutables.scala │ │ │ ├── coolSangria │ │ │ ├── FromInputImplicit.scala │ │ │ ├── ManualMarshallerHelpers.scala │ │ │ └── Sangria.scala │ │ │ ├── crypto │ │ │ └── Crypto.scala │ │ │ ├── debug │ │ │ └── DebugMacros.scala │ │ │ ├── exceptions │ │ │ └── ExceptionStacktraceToString.scala │ │ │ ├── json │ │ │ ├── Json.scala │ │ │ └── PlaySprayConversions.scala │ │ │ └── performance │ │ │ └── TimeHelper.scala │ │ └── test │ │ └── scala │ │ └── cool │ │ └── graph │ │ ├── TransactionSpec.scala │ │ ├── UtilsSpec.scala │ │ ├── client │ │ └── database │ │ │ └── GlobalDatabaseManagerSpec.scala │ │ ├── deprecated │ │ └── packageMocks │ │ │ └── PackageParserSpec │ │ │ └── PackageParserSpec.scala │ │ ├── functions │ │ └── lambda │ │ │ └── LambdaLogsSpec.scala │ │ └── util │ │ ├── AwaitUtils.scala │ │ └── JsonStringExtensionsSpec.scala ├── backend-workers │ ├── build.sbt │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── application.conf │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── worker │ │ │ ├── WorkerMain.scala │ │ │ ├── WorkerServer.scala │ │ │ ├── helpers │ │ │ └── FunctionLogsErrorShovel.scala │ │ │ ├── payloads │ │ │ ├── JsonConversions.scala │ │ │ └── Payloads.scala │ │ │ ├── services │ │ │ └── WorkerServices.scala │ │ │ ├── utils │ │ │ ├── Env.scala │ │ │ └── Utils.scala │ │ │ └── workers │ │ │ ├── FunctionLogsWorker.scala │ │ │ ├── WebhookDelivererWorker.scala │ │ │ └── Worker.scala │ │ └── test │ │ └── scala │ │ └── cool │ │ └── graph │ │ └── worker │ │ ├── SpecHelper.scala │ │ └── workers │ │ ├── FunctionLogsWorkerSpec.scala │ │ └── WebhookDelivererWorkerSpec.scala ├── build.sbt ├── client-shared │ ├── build.sbt │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── application.conf │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ ├── ArgumentSchema.scala │ │ │ ├── ClientMutation.scala │ │ │ ├── ClientMutationDefinition.scala │ │ │ ├── ClientMutationRunner.scala │ │ │ ├── MutactionGroup.scala │ │ │ ├── authProviders │ │ │ ├── Auth0AuthProviderManager.scala │ │ │ ├── AuthProviderManager.scala │ │ │ ├── DigitsAuthProviderManager.scala │ │ │ └── EmailAuthProviderManager.scala │ │ │ ├── client │ │ │ ├── CommonClientDependencies.scala │ │ │ ├── GlobalApiEndpointManager.scala │ │ │ ├── ImportExport │ │ │ │ ├── BulkExport.scala │ │ │ │ ├── BulkImport.scala │ │ │ │ └── package.scala │ │ │ ├── ProjectLockdownMiddleware.scala │ │ │ ├── adapters │ │ │ │ └── GraphcoolDataTypes.scala │ │ │ ├── authorization │ │ │ │ ├── Auth0Jwt.scala │ │ │ │ ├── ClientAuthImpl.scala │ │ │ │ ├── ModelPermissions.scala │ │ │ │ ├── PermissionValidator.scala │ │ │ │ ├── Permissions.scala │ │ │ │ ├── RelationMutationPermissions.scala │ │ │ │ └── queryPermissions │ │ │ │ │ └── QueryPermissionValidator.scala │ │ │ ├── database │ │ │ │ ├── CheckScalarFieldPermissionsDeferredResolver.scala │ │ │ │ ├── CountManyModelDeferredResolver.scala │ │ │ │ ├── CountToManyDeferredResolver.scala │ │ │ │ ├── DeferredResolverProvider.scala │ │ │ │ ├── DeferredUtils.scala │ │ │ │ ├── GetFieldFromSQLUniqueException.scala │ │ │ │ ├── ManyModelDeferredResolver.scala │ │ │ │ ├── ManyModelExistsDeferredResolver.scala │ │ │ │ ├── ToManyDeferredResolver.scala │ │ │ │ └── ToOneDeferredResolver.scala │ │ │ ├── files │ │ │ │ └── FileUploader.scala │ │ │ ├── finder │ │ │ │ ├── CachedProjectFetcherImpl.scala │ │ │ │ ├── ProjectFetcher.scala │ │ │ │ └── ProjectFetcherImpl.scala │ │ │ ├── metrics │ │ │ │ ├── ApiMetricsMiddleware.scala │ │ │ │ └── ClientSharedMetrics.scala │ │ │ ├── mutactions │ │ │ │ ├── ActionWebhookForCreateDataItemAsync.scala │ │ │ │ ├── ActionWebhookForCreateDataItemSync.scala │ │ │ │ ├── ActionWebhookForDeleteDataItemAsync.scala │ │ │ │ ├── ActionWebhookForDeleteDataItemSync.scala │ │ │ │ ├── ActionWebhookForUpdateDataItemAsync.scala │ │ │ │ ├── ActionWebhookForUpdateDataItemSync.scala │ │ │ │ ├── ActionWebhookMutaction.scala │ │ │ │ ├── AddDataItemToManyRelation.scala │ │ │ │ ├── CreateDataItem.scala │ │ │ │ ├── DeleteDataItem.scala │ │ │ │ ├── PublishSubscriptionEvent.scala │ │ │ │ ├── RemoveDataItemFromManyRelationByFromId.scala │ │ │ │ ├── RemoveDataItemFromManyRelationByToId.scala │ │ │ │ ├── RemoveDataItemFromRelationByField.scala │ │ │ │ ├── RemoveDataItemFromRelationById.scala │ │ │ │ ├── RemoveDataItemFromRelationByToAndFromField.scala │ │ │ │ ├── S3DeleteFIle.scala │ │ │ │ ├── S3UpdateFileName.scala │ │ │ │ ├── ServerSideSubscription.scala │ │ │ │ ├── SyncDataItemToAlgolia.scala │ │ │ │ ├── SyncModelToAlgolia.scala │ │ │ │ ├── UpdateDataItem.scala │ │ │ │ └── validation │ │ │ │ │ ├── ConstraintValueValidation.scala │ │ │ │ │ └── InputValueValidation.scala │ │ │ ├── mutations │ │ │ │ ├── ActionWebhooks.scala │ │ │ │ ├── AddToRelation.scala │ │ │ │ ├── AlgoliaSyncQueries.scala │ │ │ │ ├── CoolArgs.scala │ │ │ │ ├── Create.scala │ │ │ │ ├── Delete.scala │ │ │ │ ├── RemoveFromRelation.scala │ │ │ │ ├── SetRelation.scala │ │ │ │ ├── SqlMutactions.scala │ │ │ │ ├── SubscriptionEvents.scala │ │ │ │ ├── UnsetRelation.scala │ │ │ │ ├── Update.scala │ │ │ │ ├── UpdateOrCreate.scala │ │ │ │ └── definitions │ │ │ │ │ ├── CreateDefinition.scala │ │ │ │ │ ├── DeleteDefinition.scala │ │ │ │ │ ├── RelationDefinitions.scala │ │ │ │ │ ├── UpdateDefinition.scala │ │ │ │ │ └── UpdateOrCreateDefinition.scala │ │ │ ├── requestPipeline │ │ │ │ ├── FunctionExecutor.scala │ │ │ │ └── RequestPipelineRunner.scala │ │ │ ├── schema │ │ │ │ ├── InputTypesBuilder.scala │ │ │ │ ├── SchemaBuilder.scala │ │ │ │ ├── relay │ │ │ │ │ ├── RelayResolveOutput.scala │ │ │ │ │ └── RelaySchemaModelObjectTypeBuilder.scala │ │ │ │ └── simple │ │ │ │ │ └── SimpleArgumentSchema.scala │ │ │ └── server │ │ │ │ ├── ClientServer.scala │ │ │ │ ├── GraphQlRequestHandler.scala │ │ │ │ ├── HealthChecks.scala │ │ │ │ ├── IntrospectionQueryHandler.scala │ │ │ │ ├── ProjectSchemaBuilder.scala │ │ │ │ ├── RequestHandler.scala │ │ │ │ └── RequestLifecycle.scala │ │ │ ├── deprecated │ │ │ └── actions │ │ │ │ └── MutationCallbackSchemaExecutor.scala │ │ │ ├── private_api │ │ │ ├── PrivateClientApi.scala │ │ │ ├── mutations │ │ │ │ ├── PrivateMutation.scala │ │ │ │ └── SyncModelToAlgoliaMutation.scala │ │ │ └── schema │ │ │ │ ├── PrivateSchemaBuilder.scala │ │ │ │ └── SyncModelToAlgolia.scala │ │ │ ├── relay │ │ │ └── schema │ │ │ │ └── RelayArgumentSchema.scala │ │ │ ├── subscriptions │ │ │ └── SubscriptionExecutor.scala │ │ │ ├── util │ │ │ └── PrettyStrings.scala │ │ │ └── webhook │ │ │ ├── Webhook.scala │ │ │ └── WebhookCaller.scala │ │ └── test │ │ └── scala │ │ └── cool │ │ └── graph │ │ ├── adapters │ │ ├── GCDBStringEndToEndSpec.scala │ │ ├── GCDBValueConverterSpec.scala │ │ ├── GCDBValueEndToEndSpec.scala │ │ ├── GCJsonConverterSpec.scala │ │ ├── GCSangriaValuesConverterSpec.scala │ │ ├── GCStringConverterSpec.scala │ │ ├── GCStringDBConverterSpec.scala │ │ ├── GCStringEndToEndSpec.scala │ │ ├── JsStringToGCValueSpec.scala │ │ └── StringSangriaValuesConverterSpec.scala │ │ └── private_api │ │ └── finder │ │ └── CachedProjectFetcherImplSpec.scala ├── docker-compose │ ├── backend-dev-ramdisk.yml │ ├── backend-dev.yml │ ├── debug-cluster.yml │ └── ramdisk.sh ├── env_example ├── libs │ ├── akka-utils │ │ ├── build.sbt │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ ├── akkautil │ │ │ │ ├── LogUnhandled.scala │ │ │ │ ├── LogUnhandledExceptions.scala │ │ │ │ ├── SingleThreadedActorSystem.scala │ │ │ │ ├── http │ │ │ │ │ ├── Routes.scala │ │ │ │ │ ├── Server.scala │ │ │ │ │ ├── ServerExecutor.scala │ │ │ │ │ └── SimpleHttpClient.scala │ │ │ │ ├── stream │ │ │ │ │ └── OnCompleteStage.scala │ │ │ │ └── throttler │ │ │ │ │ └── Throttler.scala │ │ │ │ └── twitterFutures │ │ │ │ └── TwitterFutureImplicits.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── akkautil │ │ │ ├── http │ │ │ ├── ServerExecutorSpec.scala │ │ │ └── SimpleHttpClientSpec.scala │ │ │ ├── specs2 │ │ │ ├── AcceptanceSpecification.scala │ │ │ └── AkkaTestKitSpecs2Context.scala │ │ │ └── throttler │ │ │ └── ThrottlerSpec.scala │ ├── aws │ │ ├── build.sbt │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── aws │ │ │ └── AwsInitializers.scala │ ├── bugsnag │ │ ├── build.sbt │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── bugsnag │ │ │ └── Bugsnag.scala │ ├── cache │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── cache │ │ │ │ ├── Cache.scala │ │ │ │ ├── CaffeineImplForAsyncCache.scala │ │ │ │ └── CaffeineImplForCache.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── cache │ │ │ ├── AwaitUtil.scala │ │ │ ├── CaffeineImplForAsyncCacheSpec.scala │ │ │ └── CaffeineImplForSyncCacheSpec.scala │ ├── graphql-client │ │ ├── build.sbt │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── graphql │ │ │ │ ├── GraphQlClient.scala │ │ │ │ └── GraphQlClientImpl.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── graphql │ │ │ ├── GraphQlClientSpec.scala │ │ │ └── GraphQlResponseSpec.scala │ ├── javascript-engine │ │ ├── build.sbt │ │ └── src │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── application.conf │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── javascriptEngine │ │ │ │ ├── JavascriptExecutor.scala │ │ │ │ └── lib │ │ │ │ ├── Engine.scala │ │ │ │ └── Triteme.scala │ │ │ └── tests │ │ │ └── scala │ │ │ └── JavascriptExecutorSpec.scala │ ├── jvm-profiler │ │ ├── README.md │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── profiling │ │ │ │ ├── CpuProfiler.scala │ │ │ │ ├── JvmProfiler.scala │ │ │ │ └── MemoryProfiler.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── MemoryBeanNamesTest.scala │ ├── message-bus │ │ ├── build.sbt │ │ ├── project │ │ │ └── build.properties │ │ └── src │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── application.conf │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── messagebus │ │ │ │ ├── Conversions.scala │ │ │ │ ├── MessagingInterfaces.scala │ │ │ │ ├── pubsub │ │ │ │ ├── Actors.scala │ │ │ │ ├── MappingPubSub.scala │ │ │ │ ├── Protocol.scala │ │ │ │ ├── PubSubRouter.scala │ │ │ │ ├── inmemory │ │ │ │ │ └── InMemoryAkkaPubSub.scala │ │ │ │ └── rabbit │ │ │ │ │ ├── RabbitAkkaPubSub.scala │ │ │ │ │ ├── RabbitAkkaPubSubPublisher.scala │ │ │ │ │ └── RabbitAkkaPubSubSubscriber.scala │ │ │ │ ├── queue │ │ │ │ ├── BackoffStrategy.scala │ │ │ │ ├── MappingQueue.scala │ │ │ │ ├── inmemory │ │ │ │ │ ├── Actors.scala │ │ │ │ │ └── InMemoryAkkaQueue.scala │ │ │ │ └── rabbit │ │ │ │ │ ├── MessageInfo.scala │ │ │ │ │ ├── RabbitPlainQueueConsumer.scala │ │ │ │ │ ├── RabbitQueue.scala │ │ │ │ │ ├── RabbitQueueConsumer.scala │ │ │ │ │ └── RabbitQueuePublisher.scala │ │ │ │ ├── testkits │ │ │ │ ├── DummyPubSubSubscriber.scala │ │ │ │ ├── InMemoryPubSubTestKit.scala │ │ │ │ ├── InMemoryQueueTestKit.scala │ │ │ │ ├── RabbitAkkaPubSubTestKit.scala │ │ │ │ ├── RabbitQueueTestKit.scala │ │ │ │ └── spechelpers │ │ │ │ │ └── InMemoryMessageBusTestKits.scala │ │ │ │ └── utils │ │ │ │ ├── RabbitUtils.scala │ │ │ │ └── Utils.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── messagebus │ │ │ ├── pubsub │ │ │ ├── inmemory │ │ │ │ ├── BasicPerformanceTesting.scala │ │ │ │ ├── InMemoryAkkaPubSubSpec.scala │ │ │ │ ├── PubSubRouterAltSpec.scala │ │ │ │ └── PubSubRouterSpec.scala │ │ │ └── rabbit │ │ │ │ └── RabbitAkkaPubSubSpec.scala │ │ │ ├── queue │ │ │ ├── inmemory │ │ │ │ └── InMemoryAkkaQueueSpec.scala │ │ │ └── rabbit │ │ │ │ └── RabbitQueueSpec.scala │ │ │ └── testkits │ │ │ ├── InMemoryAkkaPubSubTestKitSpec.scala │ │ │ ├── InMemoryQueueTestKitSpec.scala │ │ │ ├── RabbitAkkaPubSubTestKitSpec.scala │ │ │ └── RabbitQueueTestKitSpec.scala │ ├── metrics │ │ ├── project │ │ │ └── build.properties │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── metrics │ │ │ │ ├── ContainerMetadata.scala │ │ │ │ ├── DummyStatsDClient.scala │ │ │ │ ├── Errors.scala │ │ │ │ ├── InstanceMetadata.scala │ │ │ │ ├── Metrics.scala │ │ │ │ ├── MetricsManager.scala │ │ │ │ ├── StatsdHostLookup.scala │ │ │ │ ├── Utils.scala │ │ │ │ └── extensions │ │ │ │ └── TimeResponseDirective.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── metrics │ │ │ ├── MetricsTagSpec.scala │ │ │ └── utils │ │ │ ├── TestLiveMetricsManager.scala │ │ │ └── TestMetricsManager.scala │ ├── project │ │ └── build.properties │ ├── rabbit-processor │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.sbt │ │ ├── project │ │ │ └── build.properties │ │ ├── release-notes.md │ │ ├── src │ │ │ ├── main │ │ │ │ └── scala │ │ │ │ │ └── cool │ │ │ │ │ └── graph │ │ │ │ │ └── rabbit │ │ │ │ │ ├── Consumers.scala │ │ │ │ │ ├── Import.scala │ │ │ │ │ ├── PlainRabbit.scala │ │ │ │ │ ├── Queue.scala │ │ │ │ │ ├── RabbitExceptionHandler.scala │ │ │ │ │ └── Utils.scala │ │ │ └── test │ │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── package │ │ │ │ └── CompileSpec.scala │ │ └── version.sbt │ ├── scala-utils │ │ ├── project │ │ │ └── build.properties │ │ └── src │ │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── utils │ │ │ │ ├── future │ │ │ │ └── FutureUtils.scala │ │ │ │ └── try │ │ │ │ └── TryExtensions.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── utils │ │ │ └── future │ │ │ └── FutureUtilSpec.scala │ └── stub-server │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.sbt │ │ ├── project │ │ └── build.properties │ │ ├── release-notes.md │ │ ├── src │ │ ├── main │ │ │ └── scala │ │ │ │ └── cool │ │ │ │ └── graph │ │ │ │ └── stub │ │ │ │ ├── JustWarningsLogger.scala │ │ │ │ ├── QueryString.scala │ │ │ │ ├── SpecHelper.scala │ │ │ │ ├── Stub.scala │ │ │ │ ├── StubDsl.scala │ │ │ │ ├── StubMatching.scala │ │ │ │ └── StubServer.scala │ │ └── test │ │ │ └── scala │ │ │ └── cool │ │ │ └── graph │ │ │ └── stub │ │ │ ├── StubDslSpec.scala │ │ │ ├── StubMatchingSpec.scala │ │ │ └── StubServerSpec.scala │ │ └── version.sbt ├── localfaas │ ├── project │ │ └── build.properties │ └── src │ │ └── main │ │ ├── resources │ │ └── application.conf │ │ └── scala │ │ └── cool │ │ └── graph │ │ └── localfaas │ │ ├── LocalFaasMain.scala │ │ ├── LocalFaasServer.scala │ │ ├── Protocol.scala │ │ ├── Utils.scala │ │ └── actors │ │ ├── Conversions.scala │ │ └── MappingActor.scala ├── project │ ├── UpdateGitRepo.scala │ ├── build.properties │ ├── dependencies.scala │ ├── libs │ │ └── maven-packagecloud-wagon-idempotent.jar │ └── plugins.sbt ├── scripts │ ├── docker-build.sh │ ├── docker-compose.test.yml │ ├── kill-all-docker-containers.sh │ ├── publish-jars.sh │ └── test.sh └── single-server │ └── src │ └── main │ ├── resources │ ├── application.conf │ ├── graphiql.html │ └── logback.xml │ └── scala │ └── cool │ └── graph │ └── singleserver │ ├── Converters.scala │ ├── SingleServerDependencies.scala │ ├── SingleServerMain.scala │ └── Version.scala └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | 3 | # CLI testing 4 | cli: 5 | docker: 6 | - image: circleci/node:8 7 | steps: 8 | - checkout 9 | - run: cd cli && yarn 10 | - run: cd cli && yarn test 11 | 12 | # content deployment 13 | content: 14 | docker: 15 | - image: circleci/node:8 16 | steps: 17 | - checkout 18 | - run: sudo npm i -g @graphcool/docs-cli 19 | - run: 20 | command: .circleci/deploy-docs.sh 21 | environment: 22 | BLUE_ID: cj67nv8do36px0187fa6i2n6z 23 | GREEN_ID: cj6glidms1ego0162chqa3al4 24 | ALIAS: graphcool-docs 25 | 26 | workflows: 27 | version: 2 28 | build: 29 | jobs: 30 | - cli: 31 | filters: 32 | branches: 33 | only: 34 | - master 35 | - content: 36 | filters: 37 | branches: 38 | only: 39 | - master 40 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | **Please post usage questions or broader discussions in the [Forum](https://www.graph.cool/forum/)**. 2 | 3 | --- 4 | 5 | For **feature requests**, please fill in the next sections: 6 | 7 | **What feature are you missing?** 8 | 9 | **How could this feature look like in detail? Tradeoffs?** 10 | 11 | --- 12 | 13 | For **bug reports**, please fill in the next sections: 14 | 15 | **Current behavior** 16 | 17 | **Reproduction** 18 | 19 | > If your problem can be reproduced with a certain service definition, please create a new GitHub repository with the reproduction instructions. 20 | 21 | **Expected behavior?** 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .DS_Store 4 | 5 | *.class 6 | *.log 7 | .coursier 8 | .envrc 9 | .ivy2 10 | 11 | # Idea 12 | .iml 13 | .idea 14 | .idea_modules 15 | 16 | # sbt specific 17 | .sbt 18 | dist/* 19 | target/ 20 | lib_managed/ 21 | src_managed/ 22 | project/boot/ 23 | project/plugins/project/ 24 | 25 | # Scala-IDE specific 26 | .scala_dependencies 27 | .ensime 28 | .ensime_cache 29 | 30 | # test files 31 | /schema 32 | /request.json 33 | /docker.env 34 | 35 | # buildkite artifacts 36 | buildkite-script-* -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Read more about contributing to the CLI [here](https://github.com/graphcool/framework/blob/master/cli/CONTRIBUTING.md) 4 | -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /cli/.yarnrc: -------------------------------------------------------------------------------- 1 | workspaces-experimental true 2 | -------------------------------------------------------------------------------- /cli/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Local Development 2 | 3 | ```sh 4 | $ git clone git@github.com:graphcool/framework.git 5 | $ cd framework/cli 6 | $ yarn install 7 | $ cd packages/graphcool-cli-engine 8 | $ yarn build 9 | $ cd ../graphcool-cli-core 10 | $ yarn build 11 | $ cd ../graphcool-cli 12 | $ yarn build 13 | $ node dist/index.js 14 | ``` 15 | -------------------------------------------------------------------------------- /cli/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphcool-cli", 3 | "lockfileVersion": 1 4 | } 5 | -------------------------------------------------------------------------------- /cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "graphcool-cli", 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "dependencies": { 8 | "graphql-request": "^1.3.6", 9 | "semver": "^5.4.1" 10 | }, 11 | "scripts": { 12 | "test": "./test.sh", 13 | "precommit": "./precommit.sh" 14 | }, 15 | "devDependencies": { 16 | "husky": "^0.14.3", 17 | "lint-staged": "^4.2.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/README.md: -------------------------------------------------------------------------------- 1 | # NodeJS TS Starter 2 | 3 | 4 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/account/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags, Flags } from 'graphcool-cli-engine' 2 | 3 | export default class Account extends Command { 4 | static topic = 'account' 5 | static description = 'Display account information' 6 | static group = 'platform' 7 | async run() { 8 | await this.auth.ensureAuth() 9 | const account = await this.client.getAccount() 10 | 11 | this.out.log(`\ 12 | Email: ${account.email} 13 | Name: ${account.name}`) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/add-template/getbin.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as fs from 'fs-extra' 3 | 4 | function getPaths(bin: string): string[] { 5 | const envPath = process.env.PATH || '' 6 | const envExt = process.env.PATHEXT || '' 7 | return envPath 8 | .replace(/["]+/g, '') 9 | .split(path.delimiter) 10 | .map(function(chunk) { 11 | return envExt.split(path.delimiter).map(function(ext) { 12 | return path.join(chunk, bin + ext) 13 | }) 14 | }) 15 | .reduce(function(a, b) { 16 | return a.concat(b) 17 | }) 18 | } 19 | 20 | export async function getBinPath(bin: string): Promise { 21 | const paths = getPaths(bin) 22 | const exist = await Promise.all(paths.map(p => fs.pathExists(p))) 23 | const existsIndex = exist.findIndex(e => e) 24 | if (existsIndex > -1) { 25 | return paths[existsIndex] 26 | } 27 | 28 | return null 29 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/deploy/emptyDefinition.ts: -------------------------------------------------------------------------------- 1 | import { ProjectDefinition } from 'graphcool-cli-engine' 2 | 3 | export const emptyDefinition: ProjectDefinition = { 4 | modules: [ 5 | { 6 | name: '', 7 | content: `\ 8 | types: ./types.graphql 9 | `, 10 | files: { 11 | './types.graphql': ``, 12 | }, 13 | }, 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/deploy/test-project/src/hello.graphql: -------------------------------------------------------------------------------- 1 | type HelloPayload { 2 | message: String! 3 | } 4 | 5 | extend type Query { 6 | hello(name: String): HelloPayload 7 | } 8 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/deploy/test-project/src/hello.js: -------------------------------------------------------------------------------- 1 | module.exports = event => { 2 | return { 3 | data: { 4 | message: `Hello ${event.data.name || 'World'}` 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/deploy/test-project/types.graphql: -------------------------------------------------------------------------------- 1 | # The following types define the data model of the example service 2 | # based on which the GraphQL API is generated 3 | 4 | type User @model { 5 | id: ID! @isUnique 6 | name: String! 7 | dateOfBirth: DateTime 8 | 9 | # Uncomment below - you can declare relations between models like this 10 | 11 | # posts: [Post!]! @relation(name: "UserPosts") 12 | } 13 | 14 | 15 | # Uncomment the model below as well 16 | 17 | # type Post @model { 18 | # id: ID! @isUnique 19 | # title: String! 20 | # 21 | # # Every relation also required a back-relation (to determine 1:1, 1:n or n:m) 22 | # author: User! @relation(name: "UserPosts") 23 | # } 24 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/example-command/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags, Flags } from 'graphcool-cli-engine' 2 | 3 | export default class ExampleCommand extends Command { 4 | static topic = 'example' 5 | static command = 'command' 6 | static description = 'example command' 7 | static flags: Flags = { 8 | target: flags.string({ 9 | char: 't', 10 | description: 'Target name' 11 | }), 12 | } 13 | async run() { 14 | await this.auth.ensureAuth() 15 | let {target} = this.flags 16 | 17 | const {id} = await this.env.getTarget(target) 18 | 19 | // continue 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/export/Exporter.describe.ts: -------------------------------------------------------------------------------- 1 | import { Exporter } from './Exporter' 2 | import { Client, Config, Environment, Output } from 'graphcool-cli-engine' 3 | import * as fs from 'fs' 4 | 5 | async function load() { 6 | const config = new Config() 7 | const out = new Output(config) 8 | const env = new Environment(out, config) 9 | await env.load({}) 10 | const client = new Client(config, env, out) 11 | const exporter = new Exporter(__dirname + '/download', client, out, config) 12 | 13 | await exporter.download('cjb53jeus0bcv0139bq2liqvu') 14 | } 15 | 16 | load() 17 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/Importer.describe.ts: -------------------------------------------------------------------------------- 1 | import { Importer } from './Importer' 2 | import { Client, Config, Environment, Output } from 'graphcool-cli-engine' 3 | import * as fs from 'fs' 4 | 5 | async function load() { 6 | const types = fs.readFileSync( 7 | __dirname + '/fixtures/basic/types.graphql', 8 | 'utf-8', 9 | ) 10 | 11 | const config = new Config() 12 | const out = new Output(config) 13 | const env = new Environment(out, config) 14 | await env.load({}) 15 | const client = new Client(config, env, out) 16 | const importer = new Importer( 17 | __dirname + '/fixtures/basic/.import', 18 | types, 19 | client, 20 | out, 21 | config, 22 | ) 23 | 24 | importer.upload('cjb53jeus0bcv0139bq2liqvu') 25 | } 26 | 27 | load() 28 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/import.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/import.zip -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/import/import.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/import/import.zip -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/import/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": 0, 3 | "lists": 0, 4 | "relations": 0 5 | } 6 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/fixtures/basic/types.graphql: -------------------------------------------------------------------------------- 1 | type Post @model { 2 | id: ID! @isUnique 3 | title: String! 4 | description: String 5 | comments: [Comment!]! @relation(name: "PostComment") 6 | tags: [String!]! 7 | state: PostState! 8 | } 9 | 10 | enum PostState { 11 | Published 12 | Unpublished 13 | Reviewed 14 | } 15 | 16 | type Comment @model { 17 | id: ID! @isUnique 18 | text: String! 19 | post: Post @relation(name: "PostComment") 20 | } 21 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/normalizers/Normalizer.ts: -------------------------------------------------------------------------------- 1 | export class Normalizer {} 2 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/import/types.ts: -------------------------------------------------------------------------------- 1 | export type ValueType = 'nodes' | 'relations' | 'lists' 2 | 3 | export type ScalarType = number | string | boolean | null 4 | 5 | export interface Node { 6 | _typeName: string 7 | id: string 8 | [fieldName: string]: ScalarType | ScalarType[] 9 | } 10 | 11 | export interface RelationNode { 12 | _typeName: string 13 | id: string 14 | fieldName: string 15 | } 16 | 17 | export type RelationTuple = [RelationNode, RelationNode] 18 | 19 | export interface ImportData { 20 | valueType: ValueType 21 | values: Node[] | RelationTuple[] 22 | } 23 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/init/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as nock from 'nock' 2 | import { Config } from 'graphcool-cli-engine' 3 | import Init from './' 4 | 5 | afterAll(() => { 6 | nock.cleanAll() 7 | }) 8 | 9 | describe('init', () => { 10 | test('test project', async () => { 11 | const result = await Init.mock( 12 | //'-t', 13 | //'blank', 14 | ) 15 | expect(result.out.stdout.output).toContain('Written files:') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/list/index.ts: -------------------------------------------------------------------------------- 1 | import { Command } from 'graphcool-cli-engine' 2 | import { table, getBorderCharacters } from 'table' 3 | 4 | export default class List extends Command { 5 | static topic = 'list' 6 | static description = 'List all deployed services' 7 | static group = 'general' 8 | static aliases = ['ls'] 9 | async run() { 10 | await this.auth.ensureAuth() 11 | const projects = await this.client.fetchProjects() 12 | this.out.log(this.out.printServices(this.env.rc.targets!, projects, false)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/local/ps.ts: -------------------------------------------------------------------------------- 1 | import { Command, Flags, flags } from 'graphcool-cli-engine' 2 | import Docker from './Docker' 3 | 4 | export default class PsLocal extends Command { 5 | static topic = 'local' 6 | static command = 'ps' 7 | static description = 'List docker containers' 8 | static group = 'local' 9 | static flags: Flags = { 10 | name: flags.string({ 11 | char: 'n', 12 | description: 'Name of the cluster instance', 13 | defaultValue: 'local' 14 | }), 15 | } 16 | async run() { 17 | const docker = new Docker(this.out, this.config, this.env, this.flags.name) 18 | await docker.ps() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/local/pull.ts: -------------------------------------------------------------------------------- 1 | import { Command, Flags, flags } from 'graphcool-cli-engine' 2 | import Docker from './Docker' 3 | 4 | export default class PullLocal extends Command { 5 | static topic = 'local' 6 | static command = 'pull' 7 | static description = 'Download latest (or specific) framework cluster version' 8 | static group = 'local' 9 | static flags: Flags = { 10 | name: flags.string({ 11 | char: 'n', 12 | description: 'Name of the cluster instance', 13 | defaultValue: 'local' 14 | }), 15 | } 16 | async run() { 17 | const docker = new Docker(this.out, this.config, this.env, this.flags.name) 18 | await docker.pull() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/local/restart.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags, Flags } from 'graphcool-cli-engine' 2 | import Docker from './Docker' 3 | 4 | export default class Restart extends Command { 5 | static topic = 'local' 6 | static command = 'restart' 7 | static description = 'Restart local development cluster' 8 | static group = 'local' 9 | static flags: Flags = { 10 | name: flags.string({ 11 | char: 'n', 12 | description: 'Name of the cluster instance', 13 | defaultValue: 'local' 14 | }), 15 | } 16 | async run() { 17 | const docker = new Docker(this.out, this.config, this.env, this.flags.name) 18 | await docker.restart() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/local/stop.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags, Flags } from 'graphcool-cli-engine' 2 | import Docker from './Docker' 3 | 4 | export default class Stop extends Command { 5 | static topic = 'local' 6 | static command = 'stop' 7 | static description = 'Stop local development cluster' 8 | static group = 'local' 9 | static flags: Flags = { 10 | name: flags.string({ 11 | char: 'n', 12 | description: 'Name of the cluster instance', 13 | defaultValue: 'local' 14 | }), 15 | } 16 | async run() { 17 | const docker = new Docker(this.out, this.config, this.env, this.flags.name) 18 | await docker.stop() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/commands/local/up.test.ts: -------------------------------------------------------------------------------- 1 | import * as nock from 'nock' 2 | import Up from './up' 3 | import up from './nocks/up' 4 | 5 | jest.mock('./Docker') 6 | const Docker = require('./Docker').default 7 | Docker.mockImplementation(() => { 8 | return { 9 | up: () => { 10 | return Promise.resolve({envVars: {MASTER_TOKEN: 'token', PORT: '60000', FUNCTIONS_PORT: '60050'}, hostName: 'localhost'}) 11 | } 12 | } 13 | }) 14 | 15 | afterAll(() => { 16 | nock.cleanAll() 17 | }) 18 | 19 | describe('up', () => { 20 | test('in empty dir', async () => { 21 | up() 22 | const result = await Up.mock() 23 | expect(result.out.stdout.output).toContain('Success! Added local cluster') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/errors/InvalidTargetError.ts: -------------------------------------------------------------------------------- 1 | export class InvalidTargetError extends Error { 2 | constructor() { 3 | super( 4 | `Please provide a valid target that points to a valid cluster and service id`, 5 | ) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/errors/ServiceDoesntExistError.ts: -------------------------------------------------------------------------------- 1 | export class ServiceDoesntExistError extends Error { 2 | constructor (service: string) { 3 | super(`The service ${service} doesn't exist`) 4 | } 5 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/src/util.ts: -------------------------------------------------------------------------------- 1 | import { Region } from 'graphcool-cli-engine' 2 | import chalk from 'chalk' 3 | 4 | const devPrefix = process.env.ENV === 'DEV' ? 'dev.' : '' 5 | 6 | export const consoleURL = (token: string, projectName?: string) => 7 | `https://${devPrefix}console.graph.cool/token?token=${token}${projectName 8 | ? `&redirect=/${encodeURIComponent(projectName)}` 9 | : ''}` 10 | // export const playgroundURL = (token: string, projectName: string) => 11 | 12 | export function sortByTimestamp(a, b) { 13 | return a.timestamp < b.timestamp ? -1 : 1 14 | } 15 | 16 | /** 17 | * Print a list of [['key', 'value'],...] pairs properly padded 18 | * @param {string[][]} arr1 19 | * @param {number} spaceLeft 20 | * @param {number} spaceBetween 21 | */ 22 | 23 | export const prettyProject = p => `${chalk.bold(p.name)} (${p.id})` 24 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/test/init.js: -------------------------------------------------------------------------------- 1 | require('nock').disableNetConnect() 2 | jest.unmock('fs-extra') 3 | process.setMaxListeners(0) 4 | global.columns = 80 5 | global.testing = true -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "forceConsistentCasingInFileNames": true, 4 | "strictNullChecks": true, 5 | "sourceMap": true, 6 | "outDir": "dist", 7 | "moduleResolution": "node", 8 | "lib": ["es2017", "dom", "esnext.asynciterable"], 9 | "target": "es5" 10 | }, 11 | "exclude": ["node_modules", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-core/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest", 4 | "tslint-eslint-rules", 5 | "tslint-config-prettier" 6 | ], 7 | "rules": { 8 | "semicolon": false, 9 | "ordered-imports": false, 10 | "object-literal-sort-keys": false, 11 | "interface-name": false, 12 | "member-access": false, 13 | "no-object-literal-type-assertion": false, 14 | "no-console": [false], 15 | "no-var-requires": null, 16 | "no-submodule-imports": null, 17 | "no-invalid-template-strings": null 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | error.log 5 | plugins.json -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/README.md: -------------------------------------------------------------------------------- 1 | # graphcool-cli-engine 2 | 3 | This package contains the **engine** of the Graphcool CLI. 4 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/requests.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/cli/packages/graphcool-cli-engine/requests.js -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Environment.describer.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from './Environment' 2 | import { Output } from './Output' 3 | import { Config } from './Config' 4 | 5 | // lets write some tests 6 | 7 | function makeEnvironment() { 8 | const config = new Config() 9 | const out = new Output(config) 10 | return new Environment(out, config) 11 | } 12 | 13 | const env = makeEnvironment() 14 | const localFile = ` 15 | platformToken: 'secret-token' 16 | targets: 17 | dev: local/asdasd123 18 | clusters: 19 | local: 20 | host: http://localhost:60000 21 | token: asdf 22 | ` 23 | 24 | env.loadRCs(localFile, null) 25 | console.log(env.localRC) 26 | console.log(env.globalRC) 27 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Flags/boolean.ts: -------------------------------------------------------------------------------- 1 | import { Flag } from './index' 2 | 3 | export default function boolean(options: Flag = {}): Flag { 4 | return { 5 | ...options, 6 | parse: undefined, 7 | } 8 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Flags/number.ts: -------------------------------------------------------------------------------- 1 | import { Flag } from './index' 2 | 3 | export default function number(options: Flag = {}): Flag { 4 | return { 5 | ...options, 6 | parse: (input: string = options.defaultValue) => parseInt(input, 10), 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Flags/string.ts: -------------------------------------------------------------------------------- 1 | import { Flag } from './index' 2 | 3 | export default function string(options: Flag = {}): Flag { 4 | return { 5 | parse: (input: string = options.defaultValue) => input, 6 | ...options, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Output/actions/screen.ts: -------------------------------------------------------------------------------- 1 | function termWidth (stream: any): number { 2 | if (!stream.isTTY){ 3 | return 80 4 | } 5 | const width = stream.getWindowSize()[0] 6 | if (width < 1){ 7 | return 80 8 | } 9 | if (width < 40){ 10 | return 40 11 | } 12 | return width 13 | } 14 | 15 | export const stdtermwidth = (global as any).columns || termWidth(process.stdout) 16 | export const errtermwidth = (global as any).columns || termWidth(process.stderr) 17 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Output/interface.ts: -------------------------------------------------------------------------------- 1 | export class Output { 2 | log(...args) { 3 | console.log(args) 4 | } 5 | warn(...args) { 6 | console.warn(args) 7 | } 8 | getErrorPrefix(fileName: string, type: 'error' | 'warning' = 'error') { 9 | return `[${type.toUpperCase()}] in ${fileName}: ` 10 | } 11 | } 12 | 13 | export class TestOutput { 14 | output: string[] 15 | constructor() { 16 | this.output = [] 17 | } 18 | log(...args) { 19 | this.output = this.output.concat(args) 20 | } 21 | warn(...args) { 22 | this.output = this.output.concat(args) 23 | } 24 | getErrorPrefix(fileName: string, type: 'error' | 'warning' = 'error') { 25 | return `[${type.toUpperCase()}] in ${fileName}: ` 26 | } 27 | } 28 | 29 | export interface IOutput { 30 | warn: (...args) => void 31 | log: (...args) => void 32 | getErrorPrefix: (fileName: string, type?: 'error' | 'warning') => string 33 | } 34 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Output/util.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | 3 | export function makePartsEnclodesByCharacterBold(str: string, character: string): string { 4 | const components = str.split(character) 5 | for(let i = 0; i < components.length; i++) { 6 | if (i % 2 === 1) { 7 | components[i] = chalk.bold(components[i]) 8 | } 9 | } 10 | return components.join(chalk.bold(`\``)) 11 | } 12 | 13 | export function regionEnumToOption(regionEnum: string): string { 14 | return regionEnum.toLowerCase().replace(/_/g, '-') 15 | } 16 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Plugin/BuiltInPlugins.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as path from 'path' 3 | import { Manager } from './Manager' 4 | import { PluginPath } from './PluginPath' 5 | 6 | export default class BuiltinPlugins extends Manager { 7 | /** 8 | * list builtin plugins 9 | * @returns {PluginPath[]} 10 | */ 11 | async list (): Promise { 12 | const commandsPath = path.resolve(path.join(__dirname, '..', 'commands')) 13 | return [new PluginPath({output: this.out, type: 'builtin', path: commandsPath})] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Plugin/Manager.ts: -------------------------------------------------------------------------------- 1 | import { Output } from '../Output/index' 2 | import { Config } from '../Config' 3 | import { ParsedCommand, ParsedTopic, PluginPath } from './PluginPath' 4 | import Cache, { Group } from './Cache' 5 | 6 | export type PluginType = "builtin" | "core" | "user" | "link" 7 | 8 | export interface ParsedPlugin { 9 | topics?: ParsedTopic[] 10 | commands?: ParsedCommand[] 11 | groups?: Group[] 12 | } 13 | 14 | export class Manager { 15 | out: Output 16 | config: Config 17 | cache: Cache 18 | 19 | constructor({out, config, cache}: { out: Output, config: Config, cache: Cache }) { 20 | this.out = out 21 | this.config = config 22 | this.cache = cache 23 | } 24 | 25 | async list(): Promise { 26 | throw new Error('abstract method Manager.list') 27 | } 28 | 29 | async handleNodeVersionChange() { 30 | // user and linked will override 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/ProjectDefinition/builtin-modules.ts: -------------------------------------------------------------------------------- 1 | export const builtinModules = [ 2 | "assert", 3 | "buffer", 4 | "child_process", 5 | "cluster", 6 | "console", 7 | "constants", 8 | "crypto", 9 | "dgram", 10 | "dns", 11 | "domain", 12 | "events", 13 | "fs", 14 | "http", 15 | "https", 16 | "module", 17 | "net", 18 | "os", 19 | "path", 20 | "process", 21 | "punycode", 22 | "querystring", 23 | "readline", 24 | "repl", 25 | "stream", 26 | "string_decoder", 27 | "timers", 28 | "tls", 29 | "tty", 30 | "url", 31 | "util", 32 | "v8", 33 | "vm", 34 | "zlib" 35 | ] -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/Topic.ts: -------------------------------------------------------------------------------- 1 | import { Command } from './Command' 2 | import { Output } from './Output/index' 3 | 4 | export class Topic { 5 | 6 | static topic: string 7 | static description?: string 8 | static hidden = false 9 | static group: string 10 | 11 | commands: Array 12 | out: Output 13 | 14 | constructor(commands: Array, out: Output) { 15 | this.out = out 16 | this.commands = commands 17 | } 18 | 19 | static get id(): string { 20 | return this.topic 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/__snapshots__/Command.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`buildHelp 1`] = ` 4 | "Usage: graphcool config get 5 | 6 | this is 7 | 8 | some multiline help 9 | " 10 | `; 11 | 12 | exports[`buildHelpLine 1`] = ` 13 | Array [ 14 | "config get", 15 | null, 16 | ] 17 | `; 18 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/check.ts: -------------------------------------------------------------------------------- 1 | import { doJobs } from './StatusChecker' 2 | 3 | const { request, cachePath } = JSON.parse(process.argv[2]) 4 | 5 | doJobs(cachePath, request) 6 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/commands/index.ts: -------------------------------------------------------------------------------- 1 | import * as klaw from 'klaw-sync' 2 | 3 | export const topics = [ 4 | {name: 'help', description: 'Print available commands and usage'}, 5 | {name: 'version', description: 'Print version'} 6 | ] 7 | 8 | export const commands = klaw(__dirname, {nodir: true}) 9 | .filter(f => f.path.endsWith('.js')) 10 | .filter(f => !f.path.endsWith('.test.js')) 11 | .filter(f => f.path !== __filename) 12 | .map(f => require(f.path)) 13 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/commands/version.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Command } from '../Command' 3 | 4 | export default class Version extends Command { 5 | static topic = 'version' 6 | static description = 'show CLI version' 7 | static aliases = ['-v', 'v', '--version'] 8 | 9 | async run () { 10 | this.out.log(this.config.userAgent) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/errors/EnvDoesntExistError.ts: -------------------------------------------------------------------------------- 1 | export class EnvDoesntExistError extends Error { 2 | constructor (env: string) { 3 | super(`The environment ${env} doesn't exist in the local .graphcoolrc`) 4 | } 5 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/errors/ExitError.ts: -------------------------------------------------------------------------------- 1 | export default class ExitError extends Error { 2 | code: number 3 | 4 | constructor (code: number) { 5 | super(`Exited with code: ${code}`) 6 | this.code = code 7 | } 8 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/errors/PromptMaskError.ts: -------------------------------------------------------------------------------- 1 | export class PromptMaskError extends Error {} 2 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/errors/RequiredFlagError.ts: -------------------------------------------------------------------------------- 1 | export class RequiredFlagError extends Error { 2 | constructor (name: string) { 3 | super(`Missing required flag --${name}`) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/fs.ts: -------------------------------------------------------------------------------- 1 | import * as fse from 'fs-extra' 2 | // import { fs as memfs, vol } from 'memfs' 3 | // 4 | // if (process.env.NODE_ENV === 'test') { 5 | // reset() 6 | // } 7 | // 8 | // const fs = process.env.NODE_ENV === 'test' ? memfs : fse 9 | 10 | export default fse 11 | // 12 | // export function reset() { 13 | // vol.reset() 14 | // vol.fromJSON({ 15 | // 'test.out': '' 16 | // }, process.cwd()) 17 | // } 18 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/test/getTmpDir.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os' 2 | import * as cuid from 'scuid' 3 | import * as path from 'path' 4 | import * as fs from 'fs-extra' 5 | 6 | export const getTmpDir = () => { 7 | const dir = path.join(os.tmpdir(), cuid() + '/') 8 | fs.mkdirpSync(dir) 9 | return dir 10 | } 11 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/src/types/rc.ts: -------------------------------------------------------------------------------- 1 | export interface RC { 2 | platformToken?: string 3 | clusters?: Clusters 4 | targets: Targets 5 | } 6 | 7 | export interface Targets { 8 | [name: string]: Target 9 | } 10 | 11 | export interface Target { 12 | cluster: string 13 | id: string 14 | } 15 | 16 | export interface Clusters { 17 | default?: string 18 | [name: string]: Cluster | string | undefined 19 | } 20 | 21 | export interface Cluster { 22 | host: string 23 | faasHost: string 24 | clusterSecret: string 25 | } 26 | 27 | export interface InternalRC { 28 | platformToken?: string 29 | clusters?: Clusters 30 | targets?: InternalTargets 31 | } 32 | 33 | export interface FrameworkRC { 34 | 'graphcool-framework'?: InternalRC 35 | 'graphcool-1.0'?: any 36 | } 37 | 38 | export interface InternalTargets { 39 | [name: string]: string 40 | } 41 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/test/init.js: -------------------------------------------------------------------------------- 1 | require('nock').disableNetConnect() 2 | jest.unmock('fs-extra') 3 | process.setMaxListeners(0) 4 | global.columns = 80 5 | global.testing = true -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/test/test-project/src/hello.graphql: -------------------------------------------------------------------------------- 1 | type HelloPayload { 2 | message: String! 3 | } 4 | 5 | extend type Query { 6 | hello(name: String): HelloPayload 7 | } 8 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/test/test-project/src/hello.js: -------------------------------------------------------------------------------- 1 | module.exports = event => { 2 | return { 3 | data: { 4 | message: `Hello ${event.data.name || 'World'}` 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/test/test-project/types.graphql: -------------------------------------------------------------------------------- 1 | # The following types define the data model of the example app 2 | # based on which the GraphQL API is generated 3 | 4 | # All types need to have the three fields id, updatedAt and createdAt like this: 5 | 6 | type User implements Node { 7 | id: ID! @isUnique 8 | createdAt: DateTime! 9 | updatedAt: DateTime! 10 | } 11 | 12 | 13 | # Graphcool has one special type, the File type: 14 | # Uncommenting this type will automatically enable the File API 15 | # Read more here: 16 | # https://www.graph.cool/docs/reference/api/file-management-eer4wiang0 17 | 18 | # type File implements Node { 19 | # contentType: String! 20 | # createdAt: DateTime! 21 | # id: ID! @isUnique 22 | # name: String! 23 | # secret: String! @isUnique 24 | # size: Int! 25 | # updatedAt: DateTime! 26 | # url: String! @isUnique 27 | # } 28 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "forceConsistentCasingInFileNames": true, 4 | "strictNullChecks": true, 5 | "sourceMap": true, 6 | "outDir": "dist", 7 | "moduleResolution": "node", 8 | "lib": ["es2017", "dom", "esnext.asynciterable"], 9 | "target": "es5" 10 | }, 11 | "exclude": ["node_modules", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-eslint-rules", "tslint-config-prettier"], 3 | "rules": { 4 | "semicolon": false, 5 | "ordered-imports": false, 6 | "object-literal-sort-keys": false, 7 | "interface-name": false, 8 | "member-access": false, 9 | "no-object-literal-type-assertion": false, 10 | "no-console": [false], 11 | "no-var-requires": null, 12 | "no-submodule-imports": null, 13 | "jsdoc-format": null, 14 | "max-classes-per-file": null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.json" { 2 | const value: any; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli-engine/typings/global.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | declare namespace NodeJS { 3 | export interface Global { 4 | columns: any 5 | testing: any 6 | } 7 | } -------------------------------------------------------------------------------- /cli/packages/graphcool-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea 4 | yarn-error.log -------------------------------------------------------------------------------- /cli/packages/graphcool-cli/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as path from 'path' 4 | import * as semver from 'semver' 5 | import * as fs from 'fs-extra' 6 | import {run} from 'graphcool-cli-engine' 7 | 8 | const root = path.join(__dirname, '..') 9 | 10 | const pjson = fs.readJsonSync(path.join(root, 'package.json')) 11 | 12 | const nodeVersion = process.version.split('v')[1] 13 | if (!semver.satisfies(nodeVersion, pjson.engines.node)) { 14 | process.stderr.write(`WARNING\nWARNING Node version must be ${pjson.engines.node} to use the Graphcool CLI\nWARNING\n`) 15 | } 16 | 17 | if (nodeVersion === '8.0.0' || nodeVersion === '8.1.0') { 18 | process.stderr.write(`WARNING\nWARNING Node version ${nodeVersion} is not supported. Please use at least 8.1.2 \nWARNING\n`) 19 | } 20 | 21 | run({config: {root, mock: false}}) 22 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "forceConsistentCasingInFileNames": true, 4 | "strictNullChecks": true, 5 | "sourceMap": true, 6 | "outDir": "dist", 7 | "moduleResolution": "node", 8 | "lib": ["es2017", "dom", "esnext.asynciterable"], 9 | "target": "es5" 10 | }, 11 | "exclude": ["node_modules", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /cli/packages/graphcool-cli/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest", 4 | "tslint-eslint-rules", 5 | "tslint-config-prettier" 6 | ], 7 | "rules": { 8 | "semicolon": false, 9 | "ordered-imports": false, 10 | "object-literal-sort-keys": false, 11 | "interface-name": false, 12 | "member-access": false, 13 | "no-object-literal-type-assertion": false, 14 | "no-console": [false] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cli/precommit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd packages/graphcool-cli-engine 6 | yarn precommit 7 | #cd ../../packages/graphcool-cli-core 8 | #yarn precommit 9 | -------------------------------------------------------------------------------- /cli/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd packages/graphcool-cli-engine 6 | npm run build 7 | #npm test 8 | cd ../../packages/graphcool-cli-core 9 | npm run build 10 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/01-Overview/04-Hosting.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: phosh3thu9 3 | description: An overview of the Graphcool platform. 4 | --- 5 | 6 | # Hosting 7 | 8 | 9 | ## Hosted Platform 10 | 11 | Graphcool is available as a full-blown and production-ready hosted solution. 12 | 13 | When you're choosing this option, the regular pricing plans apply and you get all benefits of a highly-scalable infrastructure. You can manage your services through the Graphcool CLI and get additional monitoring, collaboration and other features through the Graphcool console. 14 | 15 | 16 | ## Self-Hosting & On-Premise 17 | 18 | Graphcool can also be deployed to proprietary infrastructure rather than the default infrastructure that's provided by the Graphcool platform. 19 | 20 | When you're choosing this option, you'll have full control over all the components that are powering your backend. 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/01-Overview/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Graphcool is a backend development framework. Learn about its core ideas, the architecture and how it compares to other web frameworks. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/02-Database-&-API/04-Migrations.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: oon1ox6ohf 3 | description: Overview of how to perform migrations with the GraphQL engine. 4 | --- 5 | 6 | # Migrations 7 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/02-Database-&-API/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Get an overview over the GraphQL API that's provided by Graphcool and learn everything you need to know around data modelling and migrations. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/03-Functions-&-Events/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Graphcool leverages serverless functions so you can easily extend the capabilities of your API. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/02-Concepts/04-Auth-&-Security/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn everything you need to know about authentication mechanisms and the Graphcool permission system. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/01-Functions/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: kut8kohzoo 3 | subtitle: Learn everything about serverless functions in Graphcool by practical examples. 4 | imageUrl: "http://imgur.com/a/dvsep" 5 | description: "Graphcool makes use of three different kinds of functions: Hooks, Resolver and Subscriptions. In this guide series, we'll show you how to get started with all of these and implement features like data validation & transformation, sending welcome emails or calling out to arbitrary 3rd-party APIs." 6 | --- 7 | 8 | -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/auth0-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/auth0-config.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/auth0-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/auth0-integration.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/auth0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/auth0.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/create-post-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/create-post-permissions.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/create-user-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/create-user-permissions.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/digits-auth-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/digits-auth-provider.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/digits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/digits.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/gc-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/gc-email.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: m2dw3lglyh 3 | subtitle: Learn everything about authentication and authorization (data access permissions) in Graphcool by practical examples. 4 | imageUrl: "http://imgur.com/a/dvsep" 5 | description: Graphcool offers lots of flexibility around authentication as well as an extremely powerful authorization system. In this guide series, we show you how to integrate social logins (like Facebook or GitHub) in your apps and use Graphcool permission queries to properly secure your data. 6 | --- 7 | 8 | -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/post-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/post-permissions.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/react-apollo-auth0-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/react-apollo-auth0-lock.png -------------------------------------------------------------------------------- /docs/0.x/03-Tutorials/02-Auth/user-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/03-Tutorials/02-Auth/user-permissions.png -------------------------------------------------------------------------------- /docs/0.x/04-Reference/01-Service-Definition/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn about the Graphcool project structure and configuration format as well as related concepts. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/02-Graphcool-CLI/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn how to manage a Graphcool service using the Graphcool CLI. Every command is documented in detail. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/03-Database/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Understand how Graphool abstracts away the database, how to configure your data model and perform schema migrations. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/04-GraphQL-API/graphql-variables-type-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/04-Reference/04-GraphQL-API/graphql-variables-type-name.png -------------------------------------------------------------------------------- /docs/0.x/04-Reference/04-GraphQL-API/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Graphcool generates a CRUD GraphQL API for you. Learn about all features including filtering, pagination and realtime subscriptions. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/05-Functions/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn everything about how serverless functions are used in Graphcool to extend the automatically generated API. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/06-Auth/01-Overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: ohs4aek0pe 3 | description: The auth concept at Graphcool combines flexible authentication with powerful authorization solutions. Use Auth0, Firebase Auth or any Social Login 4 | --- 5 | 6 | # Overview 7 | 8 | Graphcool comes with **powerful authentication and authorization out-of-the-box**. 9 | 10 | You can choose between a wide range of options to enable [user authentication](!alias-geekae9gah) for your service. [Authentication tokens](!alias-eip7ahqu5o) are used to authenticate users as well as server-side calls to your API. 11 | 12 | With the [authorization system](!alias-iegoo0heez), it's easy to create [powerful permission rules](!alias-iox3aqu0ee) that seamlessly integrate with authentication. 13 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/06-Auth/02-Authentication/copy-pat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/docs/0.x/04-Reference/06-Auth/02-Authentication/copy-pat.gif -------------------------------------------------------------------------------- /docs/0.x/04-Reference/06-Auth/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Graphcool offers lots of flexibility around authentication and as well as a powerful authorization system. Read all the details here. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/07-Deployment/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn how to deploy your Graphcool service to different environments. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/08-Data-Import-&-Export/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn about. 3 | --- 4 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/09-Migrate-to-Prisma/05-File-Handling.md: -------------------------------------------------------------------------------- 1 | --- 2 | alias: ahmoo9biwa 3 | description: Migrating from Graphcool to Prisma - File Handling 4 | --- 5 | 6 | # File Handling 7 | 8 | Prisma doesn't provide a dedicated API for file handling. This gives you more flexibility and allows to manage files in a way that's appropriate to your application. 9 | 10 | To learn what a file handling API based on [AWS S3](https://aws.amazon.com/s3/) can look like, check out this [example](https://github.com/prisma/prisma/tree/master/examples/archive/file-handling-s3) project. 11 | -------------------------------------------------------------------------------- /docs/0.x/04-Reference/09-Migrate-to-Prisma/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn about. 3 | --- 4 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .idea 4 | 5 | .graphcoolrc 6 | .envrc 7 | build.zip -------------------------------------------------------------------------------- /examples/0.x/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth", 3 | "version": "1.0.0", 4 | "description": "This example demonstrates how to implement an **email-password-based authentication** workflow with Graphcool. Feel free to use it as a template for your own project!", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bcryptjs": "^2.4.3", 13 | "graphcool-lib": "^0.1.4", 14 | "validator": "^9.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/0.x/auth/src/authenticate.graphql: -------------------------------------------------------------------------------- 1 | type AuthenticateUserPayload { 2 | token: String! 3 | } 4 | 5 | extend type Mutation { 6 | authenticateUser(email: String!, password: String!): AuthenticateUserPayload 7 | } -------------------------------------------------------------------------------- /examples/0.x/auth/src/loggedInUser.graphql: -------------------------------------------------------------------------------- 1 | type LoggedInUserPayload { 2 | # if `id` is `null`, it means there is not logged in user 3 | id: ID 4 | } 5 | 6 | extend type Query { 7 | loggedInUser: LoggedInUserPayload! 8 | } 9 | -------------------------------------------------------------------------------- /examples/0.x/auth/src/signup.graphql: -------------------------------------------------------------------------------- 1 | type SignupUserPayload { 2 | id: ID! 3 | token: String! 4 | } 5 | 6 | extend type Mutation { 7 | signupUser(email: String!, password: String!): SignupUserPayload 8 | } -------------------------------------------------------------------------------- /examples/0.x/auth/types.graphql: -------------------------------------------------------------------------------- 1 | type User @model { 2 | id: ID! @isUnique 3 | createdAt: DateTime! 4 | updatedAt: DateTime! 5 | 6 | email: String! @isUnique 7 | password: String! 8 | } 9 | -------------------------------------------------------------------------------- /examples/0.x/crud-api/graphcool.yml: -------------------------------------------------------------------------------- 1 | # References the data model 2 | types: ./types.graphql 3 | 4 | # Allow everything for the sake of the demo 5 | permissions: 6 | - operation: "*" 7 | 8 | -------------------------------------------------------------------------------- /examples/0.x/crud-api/types.graphql: -------------------------------------------------------------------------------- 1 | # The following types define the data model of the example app 2 | # based on which the GraphQL API is generated 3 | 4 | type User @model { 5 | id: ID! @isUnique 6 | firstName: String! 7 | lastName: String! 8 | 9 | # You can declare relations between models like this 10 | posts: [Post!]! @relation(name: "UserPosts") 11 | comments: [Comment!]! @relation(name: "UserComments") 12 | } 13 | 14 | type Post @model { 15 | id: ID! @isUnique 16 | title: String! 17 | description: String! 18 | 19 | # Every relation also required a back-relation (to determine 1:1, 1:n or n:m) 20 | author: User! @relation(name: "UserPosts") 21 | comments: [Comment!]! @relation(name: "PostComments") 22 | 23 | } 24 | 25 | type Comment @model { 26 | id: ID! @isUnique 27 | text: String! 28 | isPublished: Boolean! 29 | author: User! @relation(name: "UserComments") 30 | post: Post! @relation(name: "PostComments") 31 | } 32 | -------------------------------------------------------------------------------- /examples/0.x/env-variables-in-functions/graphcool.yml: -------------------------------------------------------------------------------- 1 | types: ./types.graphql 2 | 3 | functions: 4 | hello: 5 | type: resolver 6 | schema: ./src/hello.graphql 7 | handler: 8 | code: 9 | src: ./src/hello.js 10 | # Set the `NAME` environment variable that's 11 | # referenced at runtime by `hello.js`. 12 | environment: 13 | NAME: Alice -------------------------------------------------------------------------------- /examples/0.x/env-variables-in-functions/src/hello.graphql: -------------------------------------------------------------------------------- 1 | type HelloPayload { 2 | message: String! 3 | } 4 | 5 | extend type Query { 6 | hello(name: String): HelloPayload 7 | } 8 | -------------------------------------------------------------------------------- /examples/0.x/env-variables-in-functions/src/hello.js: -------------------------------------------------------------------------------- 1 | module.exports = event => { 2 | // Access the environment variable called `NAME` that's 3 | // defined for this function inside `graphcool.yml`. 4 | const name = event.data.name || process.env['NAME'] 5 | return { 6 | data: { 7 | message: `Hello ${name || 'World'}` 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /examples/0.x/env-variables-in-functions/types.graphql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/examples/0.x/env-variables-in-functions/types.graphql -------------------------------------------------------------------------------- /examples/0.x/full-example/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | .envrc 4 | -------------------------------------------------------------------------------- /examples/0.x/full-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webshop", 3 | "version": "1.0.0", 4 | "description": "## What it includes", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=8.3.0" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@types/bcryptjs": "^2.4.1", 16 | "@types/validator": "^6.3.0", 17 | "bcryptjs": "^2.4.3", 18 | "graphcool-lib": "^0.1.0", 19 | "graphql-request": "^1.4.0", 20 | "stripe": "^5.1.1", 21 | "validator": "^9.0.0" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^8.0.45", 25 | "@types/stripe-node": "^4.7.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/checkout/pay.graphql: -------------------------------------------------------------------------------- 1 | type PaymentPayload { 2 | success: Boolean! 3 | } 4 | 5 | extend type Mutation { 6 | pay( 7 | stripeToken: String 8 | orderId: String! 9 | ): PaymentPayload! 10 | } 11 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/checkout/setOrderItem.graphql: -------------------------------------------------------------------------------- 1 | type SetOrderItemPayload { 2 | itemId: ID 3 | amount: Int! 4 | } 5 | 6 | extend type Mutation { 7 | setOrderItem( 8 | orderId: ID! 9 | productId: ID! 10 | amount: Int! 11 | ): SetOrderItemPayload! 12 | } 13 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/email-password/authenticate.graphql: -------------------------------------------------------------------------------- 1 | type AuthenticateUserPayload { 2 | token: String! 3 | } 4 | 5 | extend type Mutation { 6 | authenticateUser(email: String!, password: String!): AuthenticateUserPayload 7 | } -------------------------------------------------------------------------------- /examples/0.x/full-example/src/email-password/loggedInUser.graphql: -------------------------------------------------------------------------------- 1 | type LoggedInUserPayload { 2 | id: ID! 3 | } 4 | 5 | extend type Query { 6 | # return user data if request contains valid authentication token 7 | loggedInUser: LoggedInUserPayload 8 | } 9 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/email-password/signup.graphql: -------------------------------------------------------------------------------- 1 | type SignupUserPayload { 2 | id: ID! 3 | token: String! 4 | } 5 | 6 | extend type Mutation { 7 | signupUser(email: String!, password: String!): SignupUserPayload 8 | } -------------------------------------------------------------------------------- /examples/0.x/full-example/src/permissions/models/Item.graphql: -------------------------------------------------------------------------------- 1 | query isOwner($user_id: ID!, $node_id: ID!) { 2 | SomeUserExists(filter: { 3 | id: $user_id 4 | orders_some: { 5 | items_some: { 6 | id: $node_id 7 | } 8 | } 9 | }) 10 | } 11 | 12 | query isOwnerCreate($user_id: ID!, $input_orderId: ID!) { 13 | SomeUserExists(filter: { 14 | id: $user_id 15 | orders_some: { 16 | id: $input_orderId 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/permissions/models/Order.graphql: -------------------------------------------------------------------------------- 1 | query isOwner($user_id: ID!, $node_id: ID!) { 2 | SomeUserExists(filter: { 3 | id: $user_id 4 | orders_some: { 5 | id: $node_id 6 | } 7 | }) 8 | } 9 | 10 | query isOwnerCreate($user_id: ID!, $input_userId: ID!) { 11 | SomeUserExists(filter: { 12 | AND: [{ 13 | id: $user_id 14 | }, { 15 | id: $input_userId 16 | }] 17 | }) 18 | } -------------------------------------------------------------------------------- /examples/0.x/full-example/src/permissions/models/User.graphql: -------------------------------------------------------------------------------- 1 | query isOwner($user_id: ID!, $node_id: ID!) { 2 | SomeUserExists(filter: { 3 | AND: [{ 4 | id: $user_id 5 | }, { 6 | id: $node_id 7 | }] 8 | }) 9 | } -------------------------------------------------------------------------------- /examples/0.x/full-example/src/permissions/relations/Items.graphql: -------------------------------------------------------------------------------- 1 | query isOwner($user_id: ID!, $basketsBasket_id: ID!) { 2 | SomeBasketExists(filter: { 3 | id: $basketsBasket_id 4 | user: { 5 | id: $user_id 6 | } 7 | }) 8 | } -------------------------------------------------------------------------------- /examples/0.x/full-example/src/permissions/relations/Orders.graphql: -------------------------------------------------------------------------------- 1 | query isOwner($user_id: ID!, $userUser_id: ID!) { 2 | SomeUserExists(filter: { 3 | AND: [{ 4 | id: $user_id 5 | }, { 6 | id: $userUser_id 7 | }] 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /examples/0.x/full-example/src/scripts/seed.js: -------------------------------------------------------------------------------- 1 | const { GraphQLClient } = require('graphql-request') 2 | 3 | const ENDPOINT = '__ENDPOINT__' 4 | const ROOT_TOKEN = '__ROOT_TOKEN__' 5 | 6 | const client = new GraphQLClient(ENDPOINT, { 7 | headers: { 8 | Authorization: `Bearer ${ROOT_TOKEN}`, 9 | }, 10 | }) 11 | 12 | const mutation = `mutation { 13 | createProduct( 14 | description: "The new shiny iPhone" 15 | name: "iPhone X" 16 | price: 1200 17 | imageUrl: "https://cdn.vox-cdn.com/uploads/chorus_image/image/56645405/iphone_x_gallery1_2017.0.jpeg" 18 | ) { 19 | id 20 | } 21 | }` 22 | 23 | client.request(mutation) 24 | .then(data => console.log(data)) 25 | .catch(err => { 26 | console.log(err) 27 | }) 28 | -------------------------------------------------------------------------------- /examples/0.x/full-example/types.graphql: -------------------------------------------------------------------------------- 1 | type User @model { 2 | id: ID! @isUnique 3 | email: String! @isUnique 4 | name: String! 5 | stripeCustomerId: String 6 | password: String! 7 | orders: [Order!]! @relation(name: "Orders") 8 | } 9 | 10 | type Order @model { 11 | id: ID! @isUnique 12 | description: String! 13 | user: User @relation(name: "Orders") 14 | items: [Item!]! @relation(name: "Items") 15 | orderStatus: OrderStatus @defaultValue(value: NEW) 16 | } 17 | 18 | type Item @model { 19 | id: ID! @isUnique 20 | product: Product @relation(name: "ProductOnItem") 21 | order: Order @relation(name: "Items") 22 | amount: Int! @default(value: 1) 23 | } 24 | 25 | type Product @model { 26 | id: ID! @isUnique 27 | items: [Item!]! @relation(name: "ProductOnItem") 28 | name: String! 29 | description: String 30 | imageUrl: String 31 | price: Int! 32 | } 33 | 34 | enum OrderStatus { 35 | NEW 36 | PAID 37 | } 38 | -------------------------------------------------------------------------------- /examples/0.x/graphcool-lib/graphcool.yml: -------------------------------------------------------------------------------- 1 | types: ./types.graphql 2 | 3 | functions: 4 | postRandomDogImage: 5 | handler: 6 | code: src/postRandomDogImage.js 7 | type: resolver 8 | schema: src/postRandomDogImage.graphql 9 | checkPostCount: 10 | handler: 11 | code: src/checkPostCount.js 12 | type: operationBefore 13 | operation: Post.create 14 | 15 | permissions: 16 | - operation: "*" 17 | 18 | -------------------------------------------------------------------------------- /examples/0.x/graphcool-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphcool-lib", 3 | "version": "1.0.0", 4 | "description": "My Graphcool Service", 5 | "dependencies": { 6 | "graphcool-lib": "^0.1.4", 7 | "isomorphic-fetch": "^2.2.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/0.x/graphcool-lib/src/checkPostCount.js: -------------------------------------------------------------------------------- 1 | import { fromEvent } from 'graphcool-lib' 2 | 3 | const MAX_POSTS = 3 4 | 5 | export default async event => { 6 | 7 | const graphcool = fromEvent(event) 8 | const api = graphcool.api('simple/v1') 9 | 10 | const { authorId } = event.data 11 | 12 | const query = ` 13 | query ($authorId: ID!){ 14 | _allPostsMeta(filter: { 15 | author: { 16 | id: $authorId 17 | } 18 | }) { 19 | count 20 | } 21 | } 22 | ` 23 | 24 | const variables = { authorId } 25 | const queryResponse = await api.request(query, variables) 26 | 27 | if (queryResponse._allPostsMeta.count >= MAX_POSTS) { 28 | return { 29 | error: `You can at most have ${MAX_POSTS} posts` 30 | } 31 | } 32 | 33 | return { 34 | data: event.data 35 | } 36 | } -------------------------------------------------------------------------------- /examples/0.x/graphcool-lib/src/postRandomDogImage.graphql: -------------------------------------------------------------------------------- 1 | type RandomDogImagePayload { 2 | url: String! 3 | } 4 | 5 | extend type Mutation { 6 | postRandomDogImage(authorId: String!): RandomDogImagePayload 7 | } -------------------------------------------------------------------------------- /examples/0.x/graphcool-lib/types.graphql: -------------------------------------------------------------------------------- 1 | type User @model { 2 | id: ID! @isUnique 3 | 4 | posts: [Post!]! @relation(name: "UserPosts") 5 | } 6 | 7 | 8 | type Post @model { 9 | id: ID! @isUnique 10 | imageURL: String! 11 | 12 | author: User! @relation(name: "UserPosts") 13 | } 14 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "isomorphic-fetch": "^2.2.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/allBreeds.graphql: -------------------------------------------------------------------------------- 1 | type AllBreedsPayload { 2 | name: String! 3 | subBreeds: [String!]! 4 | } 5 | 6 | extend type Query { 7 | allBreeds: [AllBreedsPayload!]! 8 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/allBreeds.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | const url = 'https://dog.ceo/api/breeds/list/all' 4 | 5 | module.exports = () => { 6 | return fetch(url) 7 | .then(response => response.json()) 8 | .then(responseData => { 9 | const breedsListData = responseData.message 10 | const allBreeds = [] 11 | Object.keys(breedsListData).map(breedName => { 12 | const breed = { 13 | name: breedName, 14 | subBreeds: breedsListData[breedName] 15 | } 16 | allBreeds.push(breed) 17 | }) 18 | return { data: allBreeds } 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/allSubBreeds.graphql: -------------------------------------------------------------------------------- 1 | type AllSubBreedsPayload { 2 | name: String! 3 | } 4 | 5 | extend type Query { 6 | allSubBreeds(breedName: String!): [AllSubBreedsPayload!]! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/allSubBreeds.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | module.exports = event => { 4 | 5 | const { breedName } = event.data 6 | const url = `https://dog.ceo/api/breed/${breedName}/list` 7 | 8 | return fetch(url) 9 | .then(response => response.json()) 10 | .then(responseData => { 11 | const allSubBreedsData = responseData.message 12 | const allSubBreeds = [] 13 | allSubBreedsData.map(name => { 14 | const subBreed = { name } 15 | allSubBreeds.push(subBreed) 16 | }) 17 | return { data: allSubBreeds } 18 | }) 19 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/breedImages.graphql: -------------------------------------------------------------------------------- 1 | type BreedImagesPayload { 2 | url: String! 3 | } 4 | 5 | extend type Query { 6 | breedImages(breedName: String!): [BreedImagesPayload!]! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/breedImages.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | module.exports = event => { 4 | 5 | const { breedName } = event.data 6 | const url = `https://dog.ceo/api/breed/${breedName}/images` 7 | 8 | return fetch(url) 9 | .then(response => response.json()) 10 | .then(responseData => { 11 | const breedImagesData = responseData.message 12 | const breedImages = [] 13 | breedImagesData.map(url => { 14 | const breedImage = { url } 15 | breedImages.push(breedImage) 16 | }) 17 | return { data: breedImages } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomBreedImage.graphql: -------------------------------------------------------------------------------- 1 | type RandomBreedImagePayload { 2 | url: String! 3 | } 4 | 5 | extend type Query { 6 | randomBreedImage(breedName: String!): RandomBreedImagePayload! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomBreedImage.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | module.exports = event => { 4 | 5 | const { breedName } = event.data 6 | const url = `https://dog.ceo/api/breed/${breedName}/images/random` 7 | 8 | return fetch(url) 9 | .then(response => response.json()) 10 | .then(responseData => { 11 | const randomBreedImageData = responseData.message 12 | const randomBreedImage = { url: randomBreedImageData } 13 | return { data: randomBreedImage } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomDogImage.graphql: -------------------------------------------------------------------------------- 1 | type RandomDogImagePayload { 2 | url: String! 3 | } 4 | 5 | extend type Query { 6 | randomDogImage: RandomDogImagePayload! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomDogImage.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | const url = 'https://dog.ceo/api/breeds/image/random' 4 | 5 | module.exports = () => { 6 | return fetch(url) 7 | .then(response => response.json()) 8 | .then(responseData => { 9 | const randomDogImageData = responseData.message 10 | const randomDogImage = { url: randomDogImageData } 11 | return { data: randomDogImage } 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomSubBreedImage.graphql: -------------------------------------------------------------------------------- 1 | type RandomSubBreedImagePayload { 2 | url: String! 3 | } 4 | 5 | extend type Query { 6 | randomSubBreedImage(breedName: String!, subBreedName: String!): RandomSubBreedImagePayload! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/randomSubBreedImage.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | 4 | module.exports = event => { 5 | 6 | const { breedName, subBreedName } = event.data 7 | const url = `https://dog.ceo/api/breed/${breedName}/${subBreedName}/images/random` 8 | 9 | return fetch(url) 10 | .then(response => response.json()) 11 | .then(responseData => { 12 | const randomSubBreedImageData = responseData.message 13 | const randomSubBreedImage = { url: randomSubBreedImageData } 14 | return { data: randomSubBreedImage } 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/subBreedImages.graphql: -------------------------------------------------------------------------------- 1 | type SubBreedImagesPayload { 2 | url: String! 3 | } 4 | 5 | extend type Query { 6 | subBreedImages(breedName: String!, subBreedName: String!): [SubBreedImagesPayload!]! 7 | } -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/src/subBreedImages.js: -------------------------------------------------------------------------------- 1 | require('isomorphic-fetch') 2 | 3 | module.exports = event => { 4 | 5 | const { breedName, subBreedName } = event.data 6 | 7 | const url = `https://dog.ceo/api/breed/${breedName}/${subBreedName}/images` 8 | 9 | return fetch(url) 10 | .then(response => response.json()) 11 | .then(responseData => { 12 | const subBreedImagesData = responseData.message 13 | const subBreedImages = [] 14 | subBreedImagesData.map(url => { 15 | const subBreedImage = { url } 16 | subBreedImages.push(subBreedImage) 17 | }) 18 | return { data: subBreedImages } 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /examples/0.x/rest-wrapper/types.graphql: -------------------------------------------------------------------------------- 1 | # The following types define the data model of the example app 2 | # based on which the GraphQL API is generated 3 | 4 | # All types need to have the three fields id, updatedAt and createdAt like this: 5 | 6 | type User @model { 7 | id: ID! @isUnique 8 | createdAt: DateTime! 9 | updatedAt: DateTime! 10 | } 11 | 12 | 13 | # Graphcool has one special type, the File type: 14 | # Uncommenting this type will automatically enable the File API 15 | # Read more here: 16 | # https://www.graph.cool/docs/reference/api/file-management-eer4wiang0 17 | 18 | # type File @model { 19 | # contentType: String! 20 | # createdAt: DateTime! 21 | # id: ID! @isUnique 22 | # name: String! 23 | # secret: String! @isUnique 24 | # size: Int! 25 | # updatedAt: DateTime! 26 | # url: String! @isUnique 27 | # } 28 | -------------------------------------------------------------------------------- /examples/0.x/subscriptions/graphcool.yml: -------------------------------------------------------------------------------- 1 | types: ./types.graphql 2 | 3 | functions: 4 | createFirstArticle: 5 | type: subscription 6 | # The subscription query inside `src/createFirstArticle.graphql` determines 7 | # _when_ (i.e. upon which event) the `handler` function will be invoked. 8 | # The selection set of the subscription query further determines the _input type_ 9 | # for the `handler` function. 10 | query: src/createFirstArticle.graphql 11 | handler: 12 | code: src/createFirstArticle.js 13 | 14 | permissions: 15 | - operation: "*" -------------------------------------------------------------------------------- /examples/0.x/subscriptions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "graphcool-lib": "^0.1.4" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /examples/0.x/subscriptions/src/createFirstArticle.graphql: -------------------------------------------------------------------------------- 1 | # The subscription fires when a new `User` is created with the 2 | # `createUser`-mutation. 3 | # The _input type_ for the corresponding handler function is determined 4 | # by the selection set of the query. 5 | subscription { 6 | User(filter: { 7 | mutation_in: [CREATED] 8 | }) { 9 | node { 10 | id 11 | name 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/0.x/subscriptions/types.graphql: -------------------------------------------------------------------------------- 1 | type User @model { 2 | id: ID! @isUnique 3 | 4 | name: String! 5 | articles: [Article!]! @relation(name: "UserArticles") 6 | } 7 | 8 | type Article @model { 9 | id: ID! @isUnique 10 | 11 | title: String! 12 | author: User! @relation(name: "UserArticles") 13 | } 14 | -------------------------------------------------------------------------------- /examples/0.x/yaml-variables/graphcool.yml: -------------------------------------------------------------------------------- 1 | types: ./types.graphql 2 | 3 | functions: 4 | 5 | # This function either invokes `hey.js` or `hello.js`, 6 | # depending on the value of the environment variable `GREETING`. 7 | # If `GREETING` is not set, or set to something other than 8 | # `hello` or `hey`, then `graphcool deploy` will fail. 9 | greeting: 10 | type: resolver 11 | schema: ./src/greeting.graphql 12 | handler: 13 | code: 14 | src: ./src/${env:GREETING}.js -------------------------------------------------------------------------------- /examples/0.x/yaml-variables/src/greeting.graphql: -------------------------------------------------------------------------------- 1 | type GreetingPayload { 2 | message: String! 3 | } 4 | 5 | extend type Query { 6 | greeting(name: String): GreetingPayload 7 | } 8 | -------------------------------------------------------------------------------- /examples/0.x/yaml-variables/src/hello.js: -------------------------------------------------------------------------------- 1 | module.exports = event => { 2 | const name = event.data.name 3 | return { 4 | data: { 5 | message: `Hello ${name || 'World'}` 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /examples/0.x/yaml-variables/src/hey.js: -------------------------------------------------------------------------------- 1 | module.exports = event => { 2 | // access the environment variable called `NAME` 3 | const name = event.data.name 4 | return { 5 | data: { 6 | message: `Hey ${name || 'World'}` 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /examples/0.x/yaml-variables/types.graphql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/examples/0.x/yaml-variables/types.graphql -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Graphcool Examples 2 | 3 | Collection of Graphcool example projects 💡 4 | 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "docs-cli": "^1.0.25", 4 | "global": "^4.3.2" 5 | }, 6 | "prettier": { 7 | "semi": false, 8 | "trailingComma": "all", 9 | "singleQuote": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/.buildkite/empty-pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - label: ":tada: Nothing to do :tada:" 3 | command: "echo 'Nothing changed in /server'" -------------------------------------------------------------------------------- /server/.buildkite/hooks/pre-command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker kill $(docker ps -q) &> /dev/null 4 | echo "Stopped all old docker containers..." 5 | -------------------------------------------------------------------------------- /server/.buildkite/pipeline.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | git diff --exit-code --name-only ${BUILDKITE_COMMIT} ${BUILDKITE_COMMIT}~1 | grep "server/" 4 | if [ $? -ne 0 ]; then 5 | buildkite-agent pipeline upload ./server/.buildkite/empty-pipeline.yml 6 | else 7 | buildkite-agent pipeline upload ./server/.buildkite/pipeline.yml 8 | fi -------------------------------------------------------------------------------- /server/.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - '**Spec.scala' 4 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | *.class 4 | *.log 5 | .coursier 6 | .envrc 7 | 8 | .ivy2 9 | 10 | # Idea 11 | .idea 12 | .idea_modules 13 | 14 | # sbt specific 15 | .sbt 16 | dist/* 17 | target/ 18 | lib_managed/ 19 | src_managed/ 20 | project/boot/ 21 | project/plugins/project/ 22 | 23 | # Scala-IDE specific 24 | .scala_dependencies 25 | 26 | .ensime 27 | .ensime_cache 28 | 29 | # test files 30 | /schema 31 | /request.json 32 | /docker.env 33 | 34 | # buildkite artifacts 35 | buildkite-script-* -------------------------------------------------------------------------------- /server/.sbtopts: -------------------------------------------------------------------------------- 1 | -J-XX:MaxMetaspaceSize=2G -------------------------------------------------------------------------------- /server/.scalafmt.conf: -------------------------------------------------------------------------------- 1 | style = defaultWithAlign 2 | maxColumn = 160 3 | align = most -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # Graphcool Server 2 | The Graphcool Server is composed of a GraphQL database, the Realtime Subscriptions API, and an engine enabling asynchronous, event-driven flows using serverless functions. 3 | 4 | ## Running the tests 5 | 1. Make sure to have docker up and running 6 | 1. Make sure to make the env variables in `env_example` available, for example with `source env_example`. 7 | 1. Start all required dependencies with `docker-compose -f docker-compose/backend-dev.yml up -d --remove-orphans` 8 | 1. Run `sbt test`. This will take a few minutes. 9 | 10 | If you want to clean up afterwards, make sure to run `docker-compose -f docker-compose/backend-dev.yml down -v --remove-orphans` 11 | -------------------------------------------------------------------------------- /server/backend-api-fileupload/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-api-fileupload/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-fileupload/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/backend-api-relay/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-relay" 2 | -------------------------------------------------------------------------------- /server/backend-api-relay/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-api-relay/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-relay/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /server/backend-api-schema-manager/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-schema-manager" 2 | -------------------------------------------------------------------------------- /server/backend-api-schema-manager/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-api-schema-manager/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-schema-manager/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /server/backend-api-schema-manager/src/main/scala/SchemaManagerMain.scala: -------------------------------------------------------------------------------- 1 | import akka.actor.ActorSystem 2 | import akka.stream.ActorMaterializer 3 | import cool.graph.akkautil.http.ServerExecutor 4 | import cool.graph.bugsnag.BugSnagger 5 | import cool.graph.schemamanager.{SchemaManagerDependencies, SchemaManagerServer} 6 | import scaldi.Injectable 7 | 8 | object SchemaManagerMain extends App with Injectable { 9 | implicit val system = ActorSystem("sangria-server") 10 | implicit val materializer = ActorMaterializer() 11 | implicit val inj = SchemaManagerDependencies() 12 | implicit val bugSnagger = inject[BugSnagger] 13 | 14 | ServerExecutor(port = 8087, SchemaManagerServer("schema-manager")).startBlocking() 15 | } 16 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/README.md: -------------------------------------------------------------------------------- 1 | # Architecture Overview 2 | 3 | You can find the architecture overview [here](../backend-api-subscriptions-websocket/README.md). 4 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-simple-subscriptions" 2 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/src/main/scala/cool/graph/subscriptions/protocol/Converters.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.subscriptions.protocol 2 | 3 | import cool.graph.subscriptions.protocol.SubscriptionProtocolV05.Responses.SubscriptionSessionResponseV05 4 | import cool.graph.subscriptions.protocol.SubscriptionProtocolV07.Responses.SubscriptionSessionResponse 5 | import play.api.libs.json.Json 6 | 7 | object Converters { 8 | val converterResponse07ToString = (response: SubscriptionSessionResponse) => { 9 | import cool.graph.subscriptions.protocol.ProtocolV07.SubscriptionResponseWriters._ 10 | Json.toJson(response).toString 11 | } 12 | 13 | val converterResponse05ToString = (response: SubscriptionSessionResponseV05) => { 14 | import cool.graph.subscriptions.protocol.ProtocolV05.SubscriptionResponseWriters._ 15 | Json.toJson(response).toString 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/src/main/scala/cool/graph/subscriptions/protocol/SubscriptionRequest.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.subscriptions.protocol 2 | 3 | import cool.graph.messagebus.Conversions 4 | import play.api.libs.json.Json 5 | 6 | object SubscriptionRequest { 7 | implicit val requestFormat = Json.format[SubscriptionRequest] 8 | 9 | implicit val requestUnmarshaller = Conversions.Unmarshallers.ToJsonBackedType[SubscriptionRequest]() 10 | implicit val requestMarshaller = Conversions.Marshallers.FromJsonBackedType[SubscriptionRequest]() 11 | } 12 | 13 | case class SubscriptionRequest(sessionId: String, projectId: String, body: String) 14 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/src/main/scala/cool/graph/subscriptions/resolving/VariablesParser.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.subscriptions.resolving 2 | 3 | import spray.json._ 4 | 5 | object VariablesParser { 6 | def parseVariables(str: String): JsObject = { 7 | str.parseJson.asJsObject() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/backend-api-simple-subscriptions/src/main/scala/cool/graph/subscriptions/util/PlayJson.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.subscriptions.util 2 | 3 | import play.api.libs.json._ 4 | 5 | object PlayJson { 6 | def parse(str: String): JsResult[JsValue] = { 7 | try { 8 | JsSuccess(Json.parse(str)) 9 | } catch { 10 | case _: Exception => 11 | JsError(s"The provided string does not represent valid JSON. The string was: $str") 12 | } 13 | } 14 | 15 | def parse(bytes: Array[Byte]): JsResult[JsValue] = { 16 | try { 17 | JsSuccess(Json.parse(bytes)) 18 | } catch { 19 | case _: Exception => 20 | JsError(s"The provided byte array does not represent valid JSON.") 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-simple/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-simple" 2 | -------------------------------------------------------------------------------- /server/backend-api-simple/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-api-simple/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-simple/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/backend-api-simple/src/test/scala/cool/graph/auth2/Spec1.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.auth2 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | class Spec1 extends FlatSpec with Matchers { 6 | "bla" should "be" in { 7 | true should be(true) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/backend-api-subscriptions-websocket/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-subscriptions-websocket" -------------------------------------------------------------------------------- /server/backend-api-subscriptions-websocket/src/main/scala/cool/graph/websockets/WebsocketMain.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.websockets 2 | 3 | import akka.actor.ActorSystem 4 | import akka.stream.ActorMaterializer 5 | import cool.graph.akkautil.http.ServerExecutor 6 | import cool.graph.bugsnag.BugSnaggerImpl 7 | import cool.graph.subscriptions.websockets.services.WebsocketCloudServives 8 | 9 | object WebsocketMain extends App { 10 | implicit val system = ActorSystem("graphql-subscriptions") 11 | implicit val materializer = ActorMaterializer() 12 | implicit val bugsnag = BugSnaggerImpl(sys.env("BUGSNAG_API_KEY")) 13 | 14 | val services = WebsocketCloudServives() 15 | 16 | ServerExecutor(port = 8085, WebsocketServer(services)).startBlocking() 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-subscriptions-websocket/src/main/scala/cool/graph/websockets/metrics/SubscriptionWebsocketMetrics.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.websockets.metrics 2 | 3 | import cool.graph.metrics.MetricsManager 4 | import cool.graph.profiling.JvmProfiler 5 | 6 | object SubscriptionWebsocketMetrics extends MetricsManager { 7 | JvmProfiler.schedule(this) 8 | 9 | override def serviceName = "SubscriptionWebsocketService" 10 | 11 | val activeWsConnections = defineGauge("activeWsConnections") 12 | val incomingWebsocketMessageRate = defineCounter("incomingWebsocketMessageRate") 13 | val outgoingWebsocketMessageRate = defineCounter("outgoingWebsocketMessageRate") 14 | val incomingResponseQueueMessageRate = defineCounter("incomingResponseQueueMessageRate") 15 | } 16 | -------------------------------------------------------------------------------- /server/backend-api-subscriptions-websocket/src/main/scala/cool/graph/websockets/protocol/Request.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.websockets.protocol 2 | 3 | import cool.graph.messagebus.Conversions 4 | import play.api.libs.json.Json 5 | 6 | object Request { 7 | implicit val requestFormat = Json.format[Request] 8 | 9 | implicit val requestUnmarshaller = Conversions.Unmarshallers.ToJsonBackedType[Request]() 10 | implicit val requestMarshaller = Conversions.Marshallers.FromJsonBackedType[Request]() 11 | } 12 | 13 | case class Request(sessionId: String, projectId: String, body: String) 14 | -------------------------------------------------------------------------------- /server/backend-api-system/.sbtopts: -------------------------------------------------------------------------------- 1 | -J-XX:MaxMetaspaceSize=512M -------------------------------------------------------------------------------- /server/backend-api-system/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-api-system" 2 | -------------------------------------------------------------------------------- /server/backend-api-system/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-api-system/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 3 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0") 4 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/SystemMain.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system 2 | 3 | import akka.actor.ActorSystem 4 | import akka.stream.ActorMaterializer 5 | import cool.graph.akkautil.http.ServerExecutor 6 | 7 | import scala.language.postfixOps 8 | 9 | object SystemMain extends App { 10 | implicit val system: ActorSystem = ActorSystem("sangria-server") 11 | implicit val materializer: ActorMaterializer = ActorMaterializer() 12 | implicit val inj: SystemInjector = new SystemInjectorImpl() 13 | 14 | ServerExecutor(8081, SystemServer(inj.schemaBuilder, "system")).startBlocking() 15 | } 16 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/finder/ProjectResolver.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.finder 2 | 3 | import cool.graph.shared.models.{Project, ProjectWithClientId} 4 | 5 | import scala.concurrent.Future 6 | 7 | trait ProjectResolver { 8 | def resolve(projectIdOrAlias: String): Future[Option[Project]] 9 | def resolveProjectWithClientId(projectIdOrAlias: String): Future[Option[ProjectWithClientId]] 10 | } 11 | 12 | trait CachedProjectResolver extends ProjectResolver { 13 | 14 | /** 15 | * Invalidates the cache entry for the given project id or alias. 16 | */ 17 | def invalidate(projectIdOrAlias: String): Future[Unit] 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/ActionHandlerWebhook.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class ActionHandlerWebhook( 6 | id: String, 7 | actionId: String, 8 | url: String, 9 | isAsync: Boolean 10 | ) 11 | 12 | class ActionHandlerWebhookTable(tag: Tag) extends Table[ActionHandlerWebhook](tag, "ActionHandlerWebhook") { 13 | 14 | def id = column[String]("id", O.PrimaryKey) 15 | 16 | def actionId = column[String]("actionId") 17 | def action = 18 | foreignKey("fk_ActionHandlerWebhook_Action_actionId", actionId, Tables.Actions)(_.id) 19 | 20 | def url = column[String]("url") 21 | def isAsync = column[Boolean]("isAsync") 22 | 23 | def * = 24 | (id, actionId, url, isAsync) <> ((ActionHandlerWebhook.apply _).tupled, ActionHandlerWebhook.unapply) 25 | } 26 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/Enum.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class Enum( 6 | id: String, 7 | projectId: String, 8 | name: String, 9 | values: String 10 | ) 11 | 12 | class EnumTable(tag: Tag) extends Table[Enum](tag, "Enum") { 13 | def id = column[String]("id", O.PrimaryKey) 14 | def name = column[String]("name") 15 | def values = column[String]("values") 16 | def projectId = column[String]("projectId") 17 | def project = foreignKey("enum_projectid_foreign", projectId, Tables.Projects)(_.id) 18 | 19 | def * = (id, projectId, name, values) <> ((Enum.apply _).tupled, Enum.unapply) 20 | } 21 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/FeatureToggle.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class FeatureToggle( 6 | id: String, 7 | projectId: String, 8 | name: String, 9 | isEnabled: Boolean 10 | ) 11 | 12 | class FeatureToggleTable(tag: Tag) extends Table[FeatureToggle](tag, "FeatureToggle") { 13 | def id = column[String]("id", O.PrimaryKey) 14 | def projectId = column[String]("projectId") 15 | def name = column[String]("name") 16 | def isEnabled = column[Boolean]("isEnabled") 17 | 18 | def project = foreignKey("featuretoggle_enum_projectid_foreign", projectId, Tables.Projects)(_.id) 19 | 20 | def * = (id, projectId, name, isEnabled) <> ((FeatureToggle.apply _).tupled, FeatureToggle.unapply) 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/MappedColumns.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | import spray.json.{JsArray, JsString} 5 | 6 | import scala.util.Success 7 | 8 | object MappedColumns { 9 | import cool.graph.util.json.Json._ 10 | 11 | implicit val stringListMapper = MappedColumnType.base[Seq[String], String]( 12 | list => JsArray(list.map(JsString.apply _).toVector).toString, 13 | _.tryParseJson match { 14 | case Success(json: JsArray) => json.elements.collect { case x: JsString => x.value } 15 | case _ => Seq.empty 16 | } 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/ProjectDatabase.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import cool.graph.shared.models.Region.Region 4 | import slick.jdbc.MySQLProfile.api._ 5 | 6 | case class ProjectDatabase(id: String, region: Region, name: String, isDefaultForRegion: Boolean) 7 | 8 | class ProjectDatabaseTable(tag: Tag) extends Table[ProjectDatabase](tag, "ProjectDatabase") { 9 | implicit val RegionMapper = ProjectTable.regionMapper 10 | 11 | def id = column[String]("id", O.PrimaryKey) 12 | def region = column[Region]("region") 13 | def name = column[String]("name") 14 | def isDefaultForRegion = column[Boolean]("isDefaultForRegion") 15 | 16 | def * = (id, region, name, isDefaultForRegion) <> ((ProjectDatabase.apply _).tupled, ProjectDatabase.unapply) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/RelationFieldMirror.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class RelationFieldMirror( 6 | id: String, 7 | relationId: String, 8 | fieldId: String 9 | ) 10 | 11 | class RelationFieldMirrorTable(tag: Tag) extends Table[RelationFieldMirror](tag, "RelationFieldMirror") { 12 | 13 | def id = column[String]("id", O.PrimaryKey) 14 | 15 | def relationId = column[String]("relationId") 16 | def relation = 17 | foreignKey("relationfieldmirror_relationid_foreign", relationId, Tables.Relations)(_.id) 18 | 19 | def fieldId = column[String]("fieldId") 20 | def field = 21 | foreignKey("relationfieldmirror_fieldid_foreign", fieldId, Tables.Fields)(_.id) 22 | 23 | def * = 24 | (id, relationId, fieldId) <> ((RelationFieldMirror.apply _).tupled, RelationFieldMirror.unapply) 25 | } 26 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/database/tables/RelayId.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.database.tables 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class RelayId(id: String, typeName: String) 6 | 7 | class RelayIdTable(tag: Tag) extends Table[RelayId](tag, "RelayId") { 8 | 9 | def id = column[String]("id", O.PrimaryKey) 10 | def typeName = column[String]("typeName") 11 | 12 | def * = (id, typeName) <> ((RelayId.apply _).tupled, RelayId.unapply) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/metrics/SystemMetrics.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.metrics 2 | 3 | import cool.graph.metrics.{CustomTag, MetricsManager} 4 | import cool.graph.profiling.JvmProfiler 5 | 6 | object SystemMetrics extends MetricsManager { 7 | // this is intentionally empty. Since we don't define metrics here, we need to load the object once so the profiler kicks in. 8 | // This way it does not look so ugly on the caller side. 9 | def init(): Unit = {} 10 | 11 | // CamelCase the service name read from env 12 | override def serviceName = 13 | sys.env 14 | .getOrElse("SERVICE_NAME", "SystemShared") 15 | .split("-") 16 | .map { x => 17 | x.head.toUpper + x.tail 18 | } 19 | .mkString 20 | 21 | JvmProfiler.schedule(this) 22 | } 23 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/migration/Diff.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.migration 2 | 3 | object Diff { 4 | 5 | def diff[T](current: T, updated: T): Option[T] = { 6 | diffOpt(Some(current), Some(updated)) 7 | } 8 | 9 | def diffOpt[T](current: Option[T], updated: Option[T]): Option[T] = { 10 | if (current == updated) { 11 | None 12 | } else { 13 | updated 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/migration/dataSchema/SdlSchemaParser.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.migration.dataSchema 2 | 3 | import sangria.ast.Document 4 | import sangria.parser.{QueryParser, SyntaxError} 5 | 6 | import scala.util.Try 7 | 8 | /** 9 | * Parses SDL schema files. 10 | * Accepts empty schemas 11 | */ 12 | object SdlSchemaParser { 13 | def parse(schema: String): Try[Document] = { 14 | QueryParser.parse(schema) recover { 15 | case e: SyntaxError if e.getMessage().contains("Unexpected end of input") => Document(Vector.empty) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/migration/dataSchema/Utils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.migration.dataSchema 2 | 3 | import cool.graph.system.database.SystemFields 4 | 5 | object SystemUtil { 6 | def isNotSystemField(field: String) = !generalSystemFields.contains(field) 7 | 8 | private val generalSystemFields = SystemFields.generateAll.map(_.name) 9 | } 10 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/migration/rootTokens/RootTokenDiff.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.migration.rootTokens 2 | 3 | import cool.graph.shared.models.Project 4 | 5 | case class RootTokenDiff(project: Project, newRootTokens: Vector[String]) { 6 | val oldRootTokenNames: Vector[String] = project.rootTokens.map(_.name).toVector 7 | 8 | val addedRootTokens: Vector[String] = newRootTokens diff oldRootTokenNames 9 | val removedRootTokens: Vector[String] = oldRootTokenNames diff newRootTokens 10 | 11 | val removedRootTokensIds: Vector[String] = 12 | removedRootTokens.map(rootToken => project.rootTokens.find(_.name == rootToken).getOrElse(sys.error("Logic error in RootTokenDiff")).id) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/CreateClientDatabaseForProject.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | 6 | import scala.concurrent.Future 7 | 8 | case class CreateClientDatabaseForProject(projectId: String) extends ClientSqlSchemaChangeMutaction { 9 | 10 | override def execute: Future[ClientSqlStatementResult[Any]] = 11 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.createClientDatabaseForProject(projectId = projectId))) 12 | 13 | override def rollback = Some(DeleteClientDatabaseForProject(projectId).execute) 14 | } 15 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteAllDataItems.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.Model 6 | 7 | import scala.concurrent.Future 8 | 9 | case class DeleteAllDataItems(projectId: String, model: Model) extends ClientSqlSchemaChangeMutaction { 10 | 11 | override def execute: Future[ClientSqlStatementResult[Any]] = 12 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.deleteAllDataItems(projectId, model.name))) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteAllRelations.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.Relation 6 | 7 | import scala.concurrent.Future 8 | 9 | case class DeleteAllRelations(projectId: String, relation: Relation) extends ClientSqlSchemaChangeMutaction { 10 | 11 | override def execute: Future[ClientSqlStatementResult[Any]] = 12 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.deleteAllDataItems(projectId, relation.id))) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteAllRelayIds.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.Model 6 | 7 | import scala.concurrent.Future 8 | 9 | case class DeleteAllRelayIds(projectId: String) extends ClientSqlSchemaChangeMutaction { 10 | 11 | override def execute: Future[ClientSqlStatementResult[Any]] = 12 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.deleteAllDataItems(projectId, "_RelayId"))) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteClientDatabaseForProject.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | 6 | import scala.concurrent.Future 7 | 8 | case class DeleteClientDatabaseForProject(projectId: String) extends ClientSqlSchemaChangeMutaction { 9 | override def execute: Future[ClientSqlStatementResult[Any]] = { 10 | Future.successful( 11 | ClientSqlStatementResult( 12 | sqlAction = DatabaseMutationBuilder 13 | .deleteProjectDatabase(projectId = projectId))) 14 | } 15 | 16 | override def rollback = Some(CreateClientDatabaseForProject(projectId).execute) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteColumn.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.{Field, Model} 6 | 7 | import scala.concurrent.Future 8 | 9 | case class DeleteColumn(projectId: String, model: Model, field: Field) extends ClientSqlSchemaChangeMutaction { 10 | 11 | override def execute: Future[ClientSqlStatementResult[Any]] = { 12 | Future.successful( 13 | ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.deleteColumn(projectId = projectId, tableName = model.name, columnName = field.name))) 14 | } 15 | 16 | override def rollback = Some(CreateColumn(projectId, model, field).execute) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/DeleteRelationTable.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.{Project, Relation} 6 | 7 | import scala.concurrent.Future 8 | 9 | case class DeleteRelationTable(project: Project, relation: Relation) extends ClientSqlSchemaChangeMutaction { 10 | 11 | override def execute: Future[ClientSqlStatementResult[Any]] = 12 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.dropTable(projectId = project.id, tableName = relation.id))) 13 | 14 | override def rollback = Some(CreateRelationTable(project, relation).execute) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/client/RenameTable.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.client 2 | 3 | import cool.graph._ 4 | import cool.graph.client.database.DatabaseMutationBuilder 5 | import cool.graph.shared.models.Model 6 | 7 | import scala.concurrent.Future 8 | 9 | case class RenameTable(projectId: String, model: Model, name: String) extends ClientSqlSchemaChangeMutaction { 10 | 11 | def setName(oldName: String, newName: String): Future[ClientSqlStatementResult[Any]] = 12 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.renameTable(projectId = projectId, name = oldName, newName = newName))) 13 | 14 | override def execute: Future[ClientSqlStatementResult[Any]] = setName(model.name, name) 15 | 16 | override def rollback = Some(setName(name, model.name)) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/DeleteAction.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal 2 | 3 | import cool.graph._ 4 | import cool.graph.shared.models.{Action, Project} 5 | import cool.graph.system.database.tables.{ActionTable, RelayIdTable} 6 | import slick.jdbc.MySQLProfile.api._ 7 | import slick.lifted.TableQuery 8 | 9 | import scala.concurrent.Future 10 | 11 | case class DeleteAction(project: Project, action: Action) extends SystemSqlMutaction { 12 | override def execute: Future[SystemSqlStatementResult[Any]] = { 13 | val actions = TableQuery[ActionTable] 14 | val relayIds = TableQuery[RelayIdTable] 15 | 16 | Future.successful(SystemSqlStatementResult(sqlAction = DBIO.seq(actions.filter(_.id === action.id).delete, relayIds.filter(_.id === action.id).delete))) 17 | } 18 | 19 | override def rollback = Some(new CreateAction(project, action).execute) 20 | } 21 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/DeleteAuthProvider.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal 2 | 3 | import cool.graph._ 4 | import cool.graph.shared.models.AuthProvider 5 | import cool.graph.system.database.tables.{IntegrationTable, RelayIdTable} 6 | import slick.jdbc.MySQLProfile.api._ 7 | import slick.lifted.TableQuery 8 | 9 | import scala.concurrent.Future 10 | 11 | case class DeleteAuthProvider(integration: AuthProvider) extends SystemSqlMutaction { 12 | override def execute: Future[SystemSqlStatementResult[Any]] = { 13 | val integrations = TableQuery[IntegrationTable] 14 | val relayIds = TableQuery[RelayIdTable] 15 | 16 | Future.successful( 17 | SystemSqlStatementResult(sqlAction = DBIO.seq(integrations.filter(_.id === integration.id).delete, relayIds.filter(_.id === integration.id).delete))) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/DeleteClient.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal 2 | 3 | import cool.graph._ 4 | import cool.graph.shared.models.Client 5 | import cool.graph.system.database.tables.{ClientTable, RelayIdTable} 6 | import slick.jdbc.MySQLProfile.api._ 7 | import slick.lifted.TableQuery 8 | 9 | import scala.concurrent.Future 10 | 11 | case class DeleteClient(client: Client) extends SystemSqlMutaction { 12 | override def execute: Future[SystemSqlStatementResult[Any]] = { 13 | val clients = TableQuery[ClientTable] 14 | val relayIds = TableQuery[RelayIdTable] 15 | 16 | Future.successful(SystemSqlStatementResult(sqlAction = DBIO.seq(clients.filter(_.id === client.id).delete, relayIds.filter(_.id === client.id).delete))) 17 | } 18 | 19 | override def rollback = Some(CreateClient(client).execute) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/DeleteRootToken.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal 2 | 3 | import cool.graph._ 4 | import cool.graph.shared.models.RootToken 5 | import cool.graph.system.database.tables.{RootTokenTable, RelayIdTable} 6 | import slick.jdbc.MySQLProfile.api._ 7 | import slick.lifted.TableQuery 8 | 9 | import scala.concurrent.Future 10 | 11 | case class DeleteRootToken(rootToken: RootToken) extends SystemSqlMutaction { 12 | override def execute: Future[SystemSqlStatementResult[Any]] = { 13 | val rootTokens = TableQuery[RootTokenTable] 14 | val relayIds = TableQuery[RelayIdTable] 15 | 16 | Future.successful( 17 | SystemSqlStatementResult( 18 | sqlAction = DBIO.seq( 19 | rootTokens.filter(_.id === rootToken.id).delete, 20 | relayIds.filter(_.id === rootToken.id).delete 21 | ))) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/SystemMutactionNoop.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal 2 | 3 | import cool.graph._ 4 | import slick.jdbc.MySQLProfile.api._ 5 | 6 | import scala.concurrent.Future 7 | 8 | case class SystemMutactionNoop() extends SystemSqlMutaction { 9 | 10 | override def execute = Future.successful(SystemSqlStatementResult(sqlAction = DBIO.successful(None))) 11 | 12 | override def rollback = Some(Future.successful(SystemSqlStatementResult(sqlAction = DBIO.successful(None)))) 13 | 14 | } 15 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutactions/internal/validations/URLValidation.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutactions.internal.validations 2 | 3 | import java.net.{MalformedURLException, URL} 4 | 5 | import cool.graph.shared.errors.{UserAPIErrors, UserInputErrors} 6 | 7 | object URLValidation { 8 | def getAndValidateURL(functionName: String, input: Option[String]): String = { 9 | input match { 10 | case None => 11 | throw UserAPIErrors.InvalidValue("Url") 12 | 13 | case Some(url) => 14 | try { 15 | val trimmedString = url.trim 16 | new URL(trimmedString) 17 | trimmedString 18 | } catch { 19 | case _: MalformedURLException => throw UserInputErrors.FunctionHasInvalidUrl(functionName, url) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/mutations/MutationInput.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.mutations 2 | 3 | trait MutationInput { this: Product => 4 | import shapeless._ 5 | import syntax.typeable._ 6 | 7 | def clientMutationId: Option[String] 8 | 9 | def isAnyArgumentSet(exclude: List[String] = List()): Boolean = { 10 | getCaseClassParams(this) 11 | .filter(x => !(exclude :+ "clientMutationId").contains(x._1)) 12 | .map(_._2) 13 | .map(_.cast[Option[Any]]) 14 | .collect { 15 | case Some(x: Option[Any]) => x.isDefined 16 | } exists identity 17 | } 18 | 19 | private def getCaseClassParams(cc: AnyRef): Seq[(String, Any)] = 20 | (Seq[(String, Any)]() /: cc.getClass.getDeclaredFields) { (a, f) => 21 | f.setAccessible(true) 22 | a :+ (f.getName, f.get(cc)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/AuthenticateCustomer.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.AuthenticateCustomerInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object AuthenticateCustomer { 8 | val inputFields = List( 9 | InputField("auth0IdToken", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[AuthenticateCustomerInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | AuthenticateCustomerInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | auth0IdToken = ad("auth0IdToken").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteCustomer.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteCustomerInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteCustomer { 8 | val inputFields = List( 9 | InputField("customerId", StringType, description = "") 10 | ).asInstanceOf[List[InputField[Any]]] 11 | 12 | implicit val manual = new FromInput[DeleteCustomerInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteCustomerInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | customerId = ad("customerId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteEnum.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.{DeleteEnumInput, UpdateEnumInput} 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema.{IDType, InputField, ListInputType, OptionInputType, StringType} 6 | 7 | object DeleteEnum { 8 | val inputFields = 9 | List(InputField("enumId", IDType, description = "")).asInstanceOf[List[InputField[Any]]] 10 | 11 | implicit val manual = new FromInput[DeleteEnumInput] { 12 | import cool.graph.util.coolSangria.ManualMarshallerHelpers._ 13 | 14 | val marshaller = CoercedScalaResultMarshaller.default 15 | def fromResult(node: marshaller.Node) = { 16 | DeleteEnumInput( 17 | clientMutationId = node.clientMutationId, 18 | enumId = node.requiredArgAsString("enumId") 19 | ) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteField.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteFieldInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteField { 8 | val inputFields = List( 9 | InputField("fieldId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteFieldInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteFieldInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | fieldId = ad("fieldId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteFieldConstraint.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteFieldConstraintInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema.{IDType, InputField} 6 | 7 | object DeleteFieldConstraint { 8 | val inputFields = List(InputField("constraintId", IDType, description = "")).asInstanceOf[List[InputField[Any]]] 9 | 10 | implicit val manual = new FromInput[DeleteFieldConstraintInput] { 11 | import cool.graph.util.coolSangria.ManualMarshallerHelpers._ 12 | 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | DeleteFieldConstraintInput( 16 | clientMutationId = node.clientMutationId, 17 | constraintId = node.requiredArgAsString("constraintId") 18 | ) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteFunction.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteFunctionInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteFunction { 8 | val inputFields = List( 9 | InputField("functionId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteFunctionInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteFunctionInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | functionId = ad("functionId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteModel.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteModelInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteModel { 8 | val inputFields = List( 9 | InputField("modelId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteModelInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteModelInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | modelId = ad("modelId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteProject.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteProjectInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteProject { 8 | val inputFields = List( 9 | InputField("projectId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteProjectInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteProjectInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | projectId = ad("projectId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteRelation.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.{DeleteModelInput, DeleteRelationInput} 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteRelation { 8 | val inputFields = List( 9 | InputField("relationId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteRelationInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteRelationInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | relationId = ad("relationId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/DeleteRootToken.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.DeleteRootTokenInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object DeleteRootToken { 8 | val inputFields = List( 9 | InputField("permanentAuthTokenId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[DeleteRootTokenInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | DeleteRootTokenInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | rootTokenId = ad("permanentAuthTokenId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/EjectProject.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.EjectProjectInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema.{IDType, InputField} 6 | 7 | object EjectProject { 8 | val inputFields = List(InputField("projectId", IDType, description = "")).asInstanceOf[List[InputField[Any]]] 9 | 10 | implicit val manual = new FromInput[EjectProjectInput] { 11 | val marshaller = CoercedScalaResultMarshaller.default 12 | 13 | def fromResult(node: marshaller.Node) = { 14 | val ad = node.asInstanceOf[Map[String, Any]] 15 | 16 | EjectProjectInput(ad.get("clientMutationId").map(_.asInstanceOf[String]), ad("projectId").asInstanceOf[String]) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/ExportData.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.ExportDataInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object ExportData { 8 | val inputFields = List( 9 | InputField("projectId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[ExportDataInput] { 13 | val marshaller: CoercedScalaResultMarshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node): ExportDataInput = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | ExportDataInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | projectId = ad("projectId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/GetTemporaryDeploymentUrl.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.GetTemporaryDeployUrlInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object GetTemporaryDeploymentUrl { 8 | val inputFields = List( 9 | InputField("projectId", StringType, description = "") 10 | ) 11 | 12 | implicit val fromInput = new FromInput[GetTemporaryDeployUrlInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | 15 | def fromResult(node: marshaller.Node) = { 16 | val ad = node.asInstanceOf[Map[String, Any]] 17 | 18 | GetTemporaryDeployUrlInput( 19 | projectId = ad("projectId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/ResetProjectData.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.{ResetProjectDataInput} 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object ResetProjectData { 8 | val inputFields = List( 9 | InputField("projectId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[ResetProjectDataInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | ResetProjectDataInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | projectId = ad("projectId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/fields/ResetProjectSchema.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.fields 2 | 3 | import cool.graph.system.mutations.ResetProjectSchemaInput 4 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput} 5 | import sangria.schema._ 6 | 7 | object ResetProjectSchema { 8 | val inputFields = List( 9 | InputField("projectId", StringType, description = "") 10 | ) 11 | 12 | implicit val manual = new FromInput[ResetProjectSchemaInput] { 13 | val marshaller = CoercedScalaResultMarshaller.default 14 | def fromResult(node: marshaller.Node) = { 15 | val ad = node.asInstanceOf[Map[String, Any]] 16 | 17 | ResetProjectSchemaInput( 18 | clientMutationId = ad.get("clientMutationId").flatMap(_.asInstanceOf[Option[String]]), 19 | projectId = ad("projectId").asInstanceOf[String] 20 | ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/ActionHandlerWebhook.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object ActionHandlerWebhook { 8 | lazy val Type: ObjectType[Unit, models.ActionHandlerWebhook] = ObjectType( 9 | "ActionHandlerWebhook", 10 | "This is an ActionHandlerWebhook", 11 | interfaces[Unit, models.ActionHandlerWebhook](nodeInterface), 12 | idField[Unit, models.ActionHandlerWebhook] :: 13 | fields[Unit, models.ActionHandlerWebhook]( 14 | Field("url", StringType, resolve = _.value.url), 15 | Field("isAsync", BooleanType, resolve = _.value.isAsync) 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/CustomerSource.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object CustomerSource { 8 | lazy val Type = EnumType( 9 | "CustomerSourceType", 10 | values = List( 11 | EnumValue(models.CustomerSource.LEARN_RELAY.toString, value = models.CustomerSource.LEARN_RELAY), 12 | EnumValue(models.CustomerSource.LEARN_APOLLO.toString, value = models.CustomerSource.LEARN_APOLLO), 13 | EnumValue(models.CustomerSource.DOCS.toString, value = models.CustomerSource.DOCS), 14 | EnumValue(models.CustomerSource.WAIT_LIST.toString, value = models.CustomerSource.WAIT_LIST) 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Enum.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema.{Field, ListType, ObjectType, StringType, fields, interfaces} 6 | 7 | object Enum { 8 | 9 | lazy val Type: ObjectType[SystemUserContext, models.Enum] = { 10 | ObjectType( 11 | "Enum", 12 | "This is an enum", 13 | interfaces[SystemUserContext, models.Enum](nodeInterface), 14 | idField[SystemUserContext, models.Enum] :: 15 | fields[SystemUserContext, models.Enum]( 16 | Field("name", StringType, resolve = _.value.name), 17 | Field("values", ListType(StringType), resolve = _.value.values) 18 | ) 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/FeatureToggle.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object FeatureToggle { 8 | lazy val Type: ObjectType[SystemUserContext, models.FeatureToggle] = ObjectType( 9 | "FeatureToggle", 10 | "The feature toggles of a project.", 11 | interfaces[SystemUserContext, models.FeatureToggle](nodeInterface), 12 | idField[SystemUserContext, models.FeatureToggle] :: 13 | fields[SystemUserContext, models.FeatureToggle]( 14 | Field("name", StringType, resolve = _.value.name), 15 | Field("isEnabled", BooleanType, resolve = _.value.isEnabled) 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/FieldConstraintTypeType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema.{EnumType, EnumValue} 4 | 5 | object FieldConstraintTypeType { 6 | val enum = cool.graph.shared.models.FieldConstraintType 7 | 8 | lazy val Type = EnumType( 9 | "FieldConstraintTypeType", 10 | values = List( 11 | EnumValue("STRING", value = enum.STRING), 12 | EnumValue("NUMBER", value = enum.NUMBER), 13 | EnumValue("BOOLEAN", value = enum.BOOLEAN), 14 | EnumValue("LIST", value = enum.LIST) 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/FunctionBinding.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object FunctionBinding { 8 | lazy val Type = EnumType( 9 | "FunctionBinding", 10 | values = List( 11 | EnumValue("TRANSFORM_ARGUMENT", value = models.FunctionBinding.TRANSFORM_ARGUMENT), 12 | EnumValue("PRE_WRITE", value = models.FunctionBinding.PRE_WRITE), 13 | EnumValue("TRANSFORM_PAYLOAD", value = models.FunctionBinding.TRANSFORM_PAYLOAD) 14 | ) 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/FunctionType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object FunctionType { 8 | lazy val Type = EnumType("FunctionType", 9 | values = List( 10 | EnumValue("WEBHOOK", value = models.FunctionType.WEBHOOK), 11 | EnumValue("AUTH0", value = models.FunctionType.CODE) 12 | )) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/HistogramPeriod.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.system.database.finder 4 | import sangria.schema._ 5 | 6 | object HistogramPeriod { 7 | lazy val Type = EnumType( 8 | "HistogramPeriod", 9 | values = List( 10 | EnumValue("MONTH", value = finder.HistogramPeriod.MONTH), 11 | EnumValue("WEEK", value = finder.HistogramPeriod.WEEK), 12 | EnumValue("DAY", value = finder.HistogramPeriod.DAY), 13 | EnumValue("HOUR", value = finder.HistogramPeriod.HOUR), 14 | EnumValue("HALF_HOUR", value = finder.HistogramPeriod.HALF_HOUR) 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Integration.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object Integration { 8 | lazy val Type: InterfaceType[SystemUserContext, models.Integration] = 9 | InterfaceType( 10 | "Integration", 11 | "This is an integration. Use inline fragment to get values from the concrete type: `{id ... on SearchProviderAlgolia { algoliaSchema }}`", 12 | () => 13 | fields[SystemUserContext, models.Integration]( 14 | Field("id", IDType, resolve = _.value.id), 15 | Field("isEnabled", BooleanType, resolve = _.value.isEnabled), 16 | Field("name", IntegrationNameType.Type, resolve = _.value.name), 17 | Field("type", IntegrationTypeType.Type, resolve = _.value.integrationType) 18 | ) 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/IntegrationNameType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object IntegrationNameType { 8 | val Type = EnumType( 9 | "IntegrationNameType", 10 | values = List( 11 | EnumValue("AUTH_PROVIDER_AUTH0", value = models.IntegrationName.AuthProviderAuth0), 12 | EnumValue("AUTH_PROVIDER_DIGITS", value = models.IntegrationName.AuthProviderDigits), 13 | EnumValue("AUTH_PROVIDER_EMAIL", value = models.IntegrationName.AuthProviderEmail), 14 | EnumValue("SEARCH_PROVIDER_ALGOLIA", value = models.IntegrationName.SearchProviderAlgolia) 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/IntegrationTypeType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object IntegrationTypeType { 8 | val Type = EnumType( 9 | "IntegrationTypeType", 10 | values = List( 11 | EnumValue("AUTH_PROVIDER", value = models.IntegrationType.AuthProvider), 12 | EnumValue("SEARCH_PROVIDER", value = models.IntegrationType.SearchProvider) 13 | ) 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/LogStatus.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object LogStatus { 8 | lazy val Type = EnumType("LogStatus", 9 | values = List( 10 | EnumValue("SUCCESS", value = models.LogStatus.SUCCESS), 11 | EnumValue("FAILURE", value = models.LogStatus.FAILURE) 12 | )) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Operation.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object Operation { 8 | val Type = EnumType( 9 | "Operation", 10 | values = List( 11 | EnumValue("READ", value = models.ModelOperation.Read), 12 | EnumValue("CREATE", value = models.ModelOperation.Create), 13 | EnumValue("UPDATE", value = models.ModelOperation.Update), 14 | EnumValue("DELETE", value = models.ModelOperation.Delete) 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/PackageDefinition.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object PackageDefinition { 8 | lazy val Type: ObjectType[SystemUserContext, models.PackageDefinition] = ObjectType( 9 | "PackageDefinition", 10 | "this is a beta feature. Expect breaking changes.", 11 | interfaces[SystemUserContext, models.PackageDefinition](nodeInterface), 12 | idField[SystemUserContext, models.PackageDefinition] :: 13 | fields[SystemUserContext, models.PackageDefinition]( 14 | Field("definition", StringType, resolve = _.value.definition), 15 | Field("name", OptionType(StringType), resolve = _.value.name) 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/PermissionQueryArgument.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models.TypeIdentifier 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object PermissionQueryArgument { 8 | lazy val Type: ObjectType[SystemUserContext, PermissionQueryArguments.PermissionQueryArgument] = 9 | ObjectType( 10 | "PermissionQueryArgument", 11 | "PermissionQueryArgument", 12 | () => 13 | fields[SystemUserContext, PermissionQueryArguments.PermissionQueryArgument]( 14 | Field("name", StringType, resolve = _.value.name), 15 | Field("typeName", StringType, resolve = ctx => TypeIdentifier.toSangriaScalarType(ctx.value.typeIdentifier).name), 16 | Field("group", StringType, resolve = _.value.group) 17 | ) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/ProjectDatabase.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema.{Field, ObjectType, StringType, fields, interfaces} 6 | 7 | object ProjectDatabase { 8 | lazy val Type: ObjectType[SystemUserContext, models.ProjectDatabase] = ObjectType( 9 | "ProjectDatabase", 10 | "This is the database for a project", 11 | interfaces[SystemUserContext, models.ProjectDatabase](nodeInterface), 12 | idField[SystemUserContext, models.ProjectDatabase] :: 13 | fields[SystemUserContext, models.ProjectDatabase]( 14 | Field("name", StringType, resolve = _.value.name), 15 | Field("region", RegionType, resolve = _.value.region) 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Region.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object Region { 8 | lazy val Type = EnumType( 9 | "Region", 10 | values = List( 11 | EnumValue("EU_WEST_1", value = models.Region.EU_WEST_1), 12 | EnumValue("AP_NORTHEAST_1", value = models.Region.AP_NORTHEAST_1), 13 | EnumValue("US_WEST_2", value = models.Region.US_WEST_2) 14 | ) 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/RelationFieldMirror.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | import sangria.relay._ 5 | 6 | import cool.graph.shared.models 7 | import cool.graph.system.SystemUserContext 8 | 9 | object RelationFieldMirror { 10 | lazy val Type: ObjectType[SystemUserContext, models.RelationFieldMirror] = 11 | ObjectType( 12 | "RelationFieldMirror", 13 | "This is a relation field mirror", 14 | interfaces[SystemUserContext, models.RelationFieldMirror](nodeInterface), 15 | idField[SystemUserContext, models.RelationFieldMirror] :: 16 | fields[SystemUserContext, models.RelationFieldMirror]( 17 | Field("fieldId", IDType, resolve = ctx => ctx.value.fieldId), 18 | Field("relationId", IDType, resolve = ctx => ctx.value.relationId) 19 | ) 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Rule.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object Rule { 8 | val Type = EnumType( 9 | "Rule", 10 | values = List(EnumValue("NONE", value = models.CustomRule.None), 11 | EnumValue("GRAPH", value = models.CustomRule.Graph), 12 | EnumValue("WEBHOOK", value = models.CustomRule.Webhook)) 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/SchemaErrorType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.errors.SystemErrors.SchemaError 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object SchemaErrorType { 8 | lazy val TheListType = ListType(Type) 9 | 10 | lazy val Type: ObjectType[SystemUserContext, SchemaError] = ObjectType( 11 | "SchemaError", 12 | "An error that occurred while validating the schema.", 13 | List.empty, 14 | fields[SystemUserContext, SchemaError]( 15 | Field("type", StringType, resolve = _.value.`type`), 16 | Field("field", OptionType(StringType), resolve = _.value.field), 17 | Field("description", StringType, resolve = _.value.description) 18 | ) 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/Seat.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import cool.graph.shared.models 4 | import cool.graph.system.SystemUserContext 5 | import sangria.schema._ 6 | 7 | object Seat { 8 | lazy val Type: ObjectType[SystemUserContext, models.Seat] = ObjectType( 9 | "Seat", 10 | "This is a seat", 11 | interfaces[SystemUserContext, models.Seat](nodeInterface), 12 | idField[SystemUserContext, models.Seat] :: 13 | fields[SystemUserContext, models.Seat]( 14 | Field("isOwner", BooleanType, resolve = _.value.isOwner), 15 | Field("email", StringType, resolve = _.value.email), 16 | Field("name", OptionType(StringType), resolve = _.value.name), 17 | Field("status", SeatStatusType, resolve = _.value.status) 18 | ) 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/SeatStatus.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object SeatStatus { 8 | val Type = EnumType( 9 | "SeatStatus", 10 | values = List( 11 | EnumValue("JOINED", value = models.SeatStatus.JOINED), 12 | EnumValue("INVITED_TO_PROJECT", value = models.SeatStatus.INVITED_TO_PROJECT), 13 | EnumValue("INVITED_TO_GRAPHCOOL", value = models.SeatStatus.INVITED_TO_GRAPHCOOL) 14 | ) 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/UserType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object UserType { 8 | val Type = EnumType("UserType", 9 | values = List(EnumValue("EVERYONE", value = models.UserType.Everyone), EnumValue("AUTHENTICATED", value = models.UserType.Authenticated))) 10 | } 11 | -------------------------------------------------------------------------------- /server/backend-api-system/src/main/scala/cool/graph/system/schema/types/rootToken.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.system.schema.types 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object rootToken { 8 | lazy val Type: ObjectType[Unit, models.RootToken] = ObjectType( 9 | "PermanentAuthToken", 10 | "Used to grant permanent access to your applications and services", 11 | interfaces[Unit, models.RootToken](nodeInterface), 12 | idField[Unit, models.RootToken] :: 13 | fields[Unit, models.RootToken]( 14 | Field("name", StringType, resolve = _.value.name), 15 | Field("token", StringType, resolve = _.value.token) 16 | ) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /server/backend-shared/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-shared" -------------------------------------------------------------------------------- /server/backend-shared/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /server/backend-shared/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/client/database/ProjectRelayIdTable.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.database 2 | 3 | import slick.jdbc.MySQLProfile.api._ 4 | 5 | case class ProjectRelayId(id: String, modelId: String) 6 | 7 | class ProjectRelayIdTable(tag: Tag, schema: String) extends Table[ProjectRelayId](tag, Some(schema), "_RelayId") { 8 | 9 | def id = column[String]("id", O.PrimaryKey) 10 | def modelId = column[String]("modelId") 11 | 12 | def * = (id, modelId) <> ((ProjectRelayId.apply _).tupled, ProjectRelayId.unapply) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/client/schema/ModelMutationType.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.schema 2 | 3 | import sangria.schema._ 4 | 5 | import cool.graph.shared.models 6 | 7 | object ModelMutationType { 8 | val Type = EnumType( 9 | "_ModelMutationType", 10 | values = List( 11 | EnumValue("CREATED", value = models.ModelMutationType.Created), 12 | EnumValue("UPDATED", value = models.ModelMutationType.Updated), 13 | EnumValue("DELETED", value = models.ModelMutationType.Deleted) 14 | ) 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/client/schema/SchemaBuilderConstants.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.schema 2 | 3 | object SchemaBuilderConstants { 4 | val mutationDepth = 3 5 | 6 | val idListSuffix = "Ids" 7 | val idSuffix = "Id" 8 | } 9 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/deprecated/actions/MutationCallbackEvent.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.deprecated.actions 2 | 3 | import cool.graph.deprecated.actions.EventJsonProtocol.jsonFormat4 4 | import spray.json.{DefaultJsonProtocol, JsObject} 5 | 6 | case class MutationCallbackEvent(id: String, url: String, payload: String, headers: JsObject = JsObject.empty) 7 | 8 | object EventJsonProtocol extends DefaultJsonProtocol { 9 | implicit val mutationCallbackEventFormat = jsonFormat4(MutationCallbackEvent) 10 | } 11 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/deprecated/actions/schemas/MutationMetaData.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.deprecated.actions.schemas 2 | 3 | case class MutationMetaData(id: String, _type: String) 4 | 5 | object MutationTypes {} 6 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/shared/BackendSharedMetrics.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.shared 2 | 3 | import cool.graph.metrics.{CustomTag, MetricsManager} 4 | 5 | object BackendSharedMetrics extends MetricsManager { 6 | 7 | // CamelCase the service name read from env 8 | override def serviceName = 9 | sys.env 10 | .getOrElse("SERVICE_NAME", "BackendShared") 11 | .split("-") 12 | .map { x => 13 | x.head.toUpper + x.tail 14 | } 15 | .mkString 16 | 17 | val sqlQueryTimer = defineTimer("sqlQueryTimer", CustomTag("projectId", recordingThreshold = 1000), CustomTag("queryName", recordingThreshold = 1000)) 18 | val requestDuration = defineTimer("requestDuration", CustomTag("projectId", recordingThreshold = 1500)) 19 | } 20 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/shared/RelationFieldMirrorColumn.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.shared 2 | 3 | import cool.graph.shared.models.{Field, Project, Relation} 4 | 5 | object RelationFieldMirrorColumn { 6 | def mirrorColumnName(project: Project, field: Field, relation: Relation): String = { 7 | val fieldModel = project.getModelByFieldId_!(field.id) 8 | val modelB = relation.modelBId 9 | val modelA = relation.modelAId 10 | fieldModel.id match { 11 | case `modelA` => s"A_${field.name}" 12 | case `modelB` => s"B_${field.name}" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/shared/algolia/Types.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.shared.algolia 2 | 3 | import spray.json._ 4 | 5 | object AlgoliaEventJsonProtocol extends DefaultJsonProtocol { 6 | implicit val eventFormat: RootJsonFormat[AlgoliaEvent] = jsonFormat7(AlgoliaEvent) 7 | } 8 | 9 | case class AlgoliaEvent( 10 | indexName: String, 11 | applicationId: String, 12 | apiKey: String, 13 | operation: String, 14 | nodeId: String, 15 | requestId: String, 16 | queryResult: String 17 | ) 18 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/shared/externalServices/TestableTime.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.shared.externalServices 2 | 3 | import org.joda.time.DateTime 4 | 5 | trait TestableTime { 6 | def DateTime: org.joda.time.DateTime 7 | } 8 | 9 | class TestableTimeImplementation extends TestableTime { 10 | override def DateTime: DateTime = org.joda.time.DateTime.now 11 | } 12 | 13 | /** 14 | * The Mock generates a DateTime the first time it is called and holds on to it. 15 | * Reusing the same mock for an entire test allows us to verify generated DateTimes 16 | */ 17 | class TestableTimeMock extends TestableTime { 18 | var cache = org.joda.time.DateTime.now 19 | def setDateTime(dateTime: DateTime) = cache = dateTime 20 | override def DateTime: DateTime = cache 21 | } 22 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/collection/ToImmutables.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.collection 2 | 3 | object ToImmutable { 4 | implicit class ToImmutableSeq[T](seq: Seq[T]) { 5 | def toImmutable: collection.immutable.Seq[T] = { 6 | collection.immutable.Seq(seq: _*) 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/coolSangria/FromInputImplicit.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.coolSangria 2 | 3 | import sangria.marshalling.{CoercedScalaResultMarshaller, FromInput, ResultMarshaller} 4 | 5 | object FromInputImplicit { 6 | 7 | implicit val DefaultScalaResultMarshaller: FromInput[Any] = new FromInput[Any] { 8 | override val marshaller: ResultMarshaller = ResultMarshaller.defaultResultMarshaller 9 | override def fromResult(node: marshaller.Node): Any = node 10 | } 11 | 12 | implicit val CoercedResultMarshaller: FromInput[Any] = new FromInput[Any] { 13 | override val marshaller: ResultMarshaller = CoercedScalaResultMarshaller.default 14 | override def fromResult(node: marshaller.Node): Any = node 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/coolSangria/Sangria.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.coolSangria 2 | 3 | import sangria.schema.Args 4 | 5 | import scala.collection.concurrent.TrieMap 6 | 7 | object Sangria { 8 | 9 | def rawArgs(raw: Map[String, Any]): Args = { 10 | new Args(raw, Set.empty, Set.empty, Set.empty, TrieMap.empty) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/crypto/Crypto.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.crypto 2 | 3 | import com.github.t3hnar.bcrypt._ 4 | 5 | object Crypto { 6 | def hash(password: String): String = password.bcrypt 7 | 8 | def verify(password: String, hash: String): Boolean = password.isBcrypted(hash) 9 | } 10 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/exceptions/ExceptionStacktraceToString.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.exceptions 2 | 3 | import java.io.{PrintWriter, StringWriter} 4 | 5 | object ExceptionStacktraceToString { 6 | 7 | implicit class ThrowableStacktraceExtension(t: Throwable) { 8 | def stackTraceAsString: String = ExceptionStacktraceToString(t) 9 | } 10 | 11 | def apply(t: Throwable): String = { 12 | val sw = new StringWriter() 13 | val pw = new PrintWriter(sw) 14 | t.printStackTrace(pw) 15 | sw.toString() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-shared/src/main/scala/cool/graph/util/performance/TimeHelper.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util.performance 2 | 3 | trait TimeHelper { 4 | def time[R](measurementName: String = "")(block: => R): R = { 5 | val t0 = System.nanoTime() 6 | val result = block 7 | val t1 = System.nanoTime() 8 | val diffInMicros = (t1 - t0) / 1000 9 | val millis = diffInMicros.toDouble / 1000 10 | println(s"Elapsed time [$measurementName]: ${millis}ms") 11 | result 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-shared/src/test/scala/cool/graph/util/AwaitUtils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util 2 | 3 | import scala.concurrent.{Await, Awaitable} 4 | 5 | object AwaitUtils { 6 | def await[T](awaitable: Awaitable[T]): T = { 7 | import scala.concurrent.duration._ 8 | Await.result(awaitable, 5.seconds) 9 | } 10 | 11 | implicit class AwaitableExtension[T](awaitable: Awaitable[T]) { 12 | import scala.concurrent.duration._ 13 | def await: T = { 14 | Await.result(awaitable, 5.seconds) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/backend-shared/src/test/scala/cool/graph/util/JsonStringExtensionsSpec.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.util 2 | 3 | import cool.graph.util.json.Json._ 4 | import org.scalatest.{Matchers, WordSpec} 5 | import spray.json._ 6 | 7 | class JsonStringExtensionsSpec extends WordSpec with Matchers { 8 | 9 | "pathAs" should { 10 | "get string" in { 11 | """{"a": "b"}""".parseJson.pathAsString("a") should be("b") 12 | } 13 | 14 | "get string nested in array" in { 15 | val json = """{"a": ["b", "c"]}""".parseJson 16 | json.pathAsString("a.[0]") should be("b") 17 | json.pathAsString("a.[1]") should be("c") 18 | } 19 | 20 | "get string nested in object in array" in { 21 | val json = """{"a": [{"b":"c"}, {"b":"d"}]}""".parseJson 22 | json.pathAsString("a.[0].b") should be("c") 23 | json.pathAsString("a.[1].b") should be("d") 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/backend-workers/build.sbt: -------------------------------------------------------------------------------- 1 | name := "backend-workers" 2 | mainClass in Compile := Some("cool.graph.worker.WorkerMain") -------------------------------------------------------------------------------- /server/backend-workers/src/main/scala/cool/graph/worker/WorkerMain.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.worker 2 | 3 | import akka.actor.ActorSystem 4 | import akka.stream.ActorMaterializer 5 | import cool.graph.akkautil.http.ServerExecutor 6 | import cool.graph.bugsnag.BugSnaggerImpl 7 | import cool.graph.worker.services.WorkerCloudServices 8 | import cool.graph.worker.utils.Env 9 | 10 | object WorkerMain extends App { 11 | implicit val bugsnagger = BugSnaggerImpl(Env.bugsangApiKey) 12 | implicit val system = ActorSystem("backend-workers") 13 | implicit val materializer = ActorMaterializer() 14 | 15 | val services = WorkerCloudServices() 16 | val serverExecutor = ServerExecutor(8090, WorkerServer(services)) 17 | 18 | serverExecutor.startBlocking() 19 | } 20 | -------------------------------------------------------------------------------- /server/backend-workers/src/main/scala/cool/graph/worker/payloads/Payloads.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.worker.payloads 2 | 3 | import play.api.libs.json.JsObject 4 | 5 | case class Webhook( 6 | projectId: String, 7 | functionId: String, 8 | requestId: String, 9 | url: String, 10 | payload: String, 11 | id: String, 12 | headers: Map[String, String] 13 | ) 14 | 15 | case class LogItem( 16 | id: String, 17 | projectId: String, 18 | functionId: String, 19 | requestId: String, 20 | status: String, 21 | duration: Long, 22 | timestamp: String, 23 | message: JsObject 24 | ) 25 | -------------------------------------------------------------------------------- /server/backend-workers/src/main/scala/cool/graph/worker/utils/Env.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.worker.utils 2 | 3 | object Env { 4 | val clusterLocalRabbitUri = sys.env("RABBITMQ_URI") 5 | val bugsangApiKey = sys.env("BUGSNAG_API_KEY") 6 | } 7 | -------------------------------------------------------------------------------- /server/backend-workers/src/main/scala/cool/graph/worker/utils/Utils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.worker.utils 2 | 3 | import org.joda.time.DateTime 4 | import org.joda.time.format.DateTimeFormat 5 | 6 | object Utils { 7 | val msqlDateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS") // mysql datetime(3) format 8 | 9 | /** 10 | * Generates a mysql datetime(3) timestamp (now) 11 | */ 12 | def msqlDateTime3Timestamp(): String = Utils.msqlDateFormatter.print(DateTime.now()) 13 | } 14 | -------------------------------------------------------------------------------- /server/backend-workers/src/main/scala/cool/graph/worker/workers/Worker.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.worker.workers 2 | 3 | import scala.concurrent.Future 4 | 5 | trait Worker { 6 | def start: Future[_] = Future.successful(()) 7 | def stop: Future[_] = Future.successful(()) 8 | } 9 | -------------------------------------------------------------------------------- /server/client-shared/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies += "com.typesafe.play" % "play-json_2.11" % "2.5.16" 2 | -------------------------------------------------------------------------------- /server/client-shared/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | privateClientApiSecret = ${PRIVATE_CLIENT_API_SECRET} -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/MutactionGroup.scala: -------------------------------------------------------------------------------- 1 | package cool.graph 2 | 3 | case class MutactionGroup(mutactions: List[Mutaction], async: Boolean) { 4 | 5 | // just for debugging! 6 | def unpackTransactions: List[Mutaction] = { 7 | mutactions.flatMap { 8 | case t: Transaction => t.clientSqlMutactions 9 | case x => Seq(x) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/GlobalApiEndpointManager.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client 2 | 3 | import cool.graph.shared.models.Region 4 | import cool.graph.shared.models.Region.Region 5 | 6 | case class GlobalApiEndpointManager(euWest1: String, usWest2: String, apNortheast1: String) { 7 | 8 | def getEndpointForProject(region: Region, projectId: String): String = { 9 | region match { 10 | case Region.EU_WEST_1 => s"${euWest1}/${projectId}" 11 | case Region.US_WEST_2 => s"${usWest2}/${projectId}" 12 | case Region.AP_NORTHEAST_1 => s"${apNortheast1}/${projectId}" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/database/CountManyModelDeferredResolver.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.database 2 | 3 | import cool.graph.client.database.DeferredTypes._ 4 | 5 | class CountManyModelDeferredResolver { 6 | def resolve(orderedDeferreds: Vector[OrderedDeferred[CountManyModelDeferred]], ctx: DataResolver): Vector[OrderedDeferredFutureResult[Int]] = { 7 | val deferreds = orderedDeferreds.map(_.deferred) 8 | 9 | DeferredUtils.checkSimilarityOfModelDeferredsAndThrow(deferreds) 10 | 11 | val headDeferred = deferreds.head 12 | val model = headDeferred.model 13 | val args = headDeferred.args 14 | 15 | val futureDataItems = ctx.countByModel(model, args) 16 | 17 | val results = orderedDeferreds.map { 18 | case OrderedDeferred(deferred, order) => 19 | OrderedDeferredFutureResult[Int](futureDataItems, order) 20 | } 21 | 22 | results 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/database/GetFieldFromSQLUniqueException.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.database 2 | 3 | import java.sql.SQLIntegrityConstraintViolationException 4 | 5 | import cool.graph.shared.mutactions.MutationTypes.ArgumentValue 6 | 7 | object GetFieldFromSQLUniqueException { 8 | 9 | def getField(values: List[ArgumentValue], e: SQLIntegrityConstraintViolationException): String = { 10 | values.filter(x => e.getCause.getMessage.contains("\'" + x.name + "_")) match { 11 | case x if x.nonEmpty => "Field name = " + x.head.name 12 | case _ => "Sorry, no more details available." 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/finder/ProjectFetcher.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.finder 2 | 3 | import cool.graph.shared.errors.UserAPIErrors 4 | import cool.graph.shared.models.ProjectWithClientId 5 | 6 | import scala.concurrent.{ExecutionContext, Future} 7 | 8 | trait ProjectFetcher { 9 | def fetch_!(projectIdOrAlias: String)(implicit ec: ExecutionContext): Future[ProjectWithClientId] = { 10 | fetch(projectIdOrAlias = projectIdOrAlias) map { 11 | case None => throw UserAPIErrors.ProjectNotFound(projectIdOrAlias) 12 | case Some(project) => project 13 | } 14 | } 15 | 16 | def fetch(projectIdOrAlias: String): Future[Option[ProjectWithClientId]] 17 | } 18 | 19 | trait RefreshableProjectFetcher extends ProjectFetcher { 20 | def fetchRefreshed(projectIdOrAlias: String): Future[Option[ProjectWithClientId]] 21 | } 22 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/mutactions/ActionWebhookMutaction.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.mutactions 2 | 3 | import cool.graph.Mutaction 4 | 5 | /** 6 | * Marker interface for ActionWebhook 7 | */ 8 | trait ActionWebhookMutaction extends Mutaction {} 9 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/mutactions/RemoveDataItemFromRelationById.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.mutactions 2 | 3 | import cool.graph.Types.Id 4 | import cool.graph._ 5 | import cool.graph.client.database.{DataResolver, DatabaseMutationBuilder} 6 | import cool.graph.shared.models.Project 7 | 8 | import scala.concurrent.Future 9 | import scala.util.Success 10 | 11 | case class RemoveDataItemFromRelationById(project: Project, relationId: String, id: Id) extends ClientSqlDataChangeMutaction { 12 | 13 | override def execute: Future[ClientSqlStatementResult[Any]] = { 14 | Future.successful(ClientSqlStatementResult(sqlAction = DatabaseMutationBuilder.deleteRelationRowById(project.id, relationId, id))) 15 | } 16 | 17 | override def rollback: Some[Future[ClientSqlStatementResult[Any]]] = Some(ClientMutactionNoop().execute) 18 | } 19 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/mutactions/S3DeleteFIle.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.mutactions 2 | 3 | import com.typesafe.scalalogging.LazyLogging 4 | import cool.graph._ 5 | import cool.graph.client.ClientInjector 6 | import cool.graph.client.files.FileUploader 7 | import cool.graph.shared.models.{Model, Project} 8 | 9 | import scala.concurrent.Future 10 | 11 | case class S3DeleteFile(model: Model, project: Project, fileSecret: String)(implicit injector: ClientInjector) extends Mutaction with LazyLogging { 12 | 13 | override def execute: Future[MutactionExecutionResult] = { 14 | 15 | val uploader = new FileUploader(project) 16 | 17 | uploader.deleteFile(project, fileSecret) 18 | 19 | Future.successful(MutactionExecutionSuccess()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/mutations/definitions/DeleteDefinition.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.mutations.definitions 2 | 3 | import cool.graph.client.SchemaBuilderUtils 4 | import cool.graph.shared.models.{Model, Project} 5 | import cool.graph.{ArgumentSchema, ClientMutationDefinition, SchemaArgument} 6 | 7 | case class DeleteDefinition(argumentSchema: ArgumentSchema, project: Project) extends ClientMutationDefinition { 8 | 9 | val argumentGroupName = "Delete" 10 | 11 | override def getSchemaArguments(model: Model): List[SchemaArgument] = { 12 | val idField = model.getFieldByName_!("id") 13 | List( 14 | SchemaArgument(idField.name, SchemaBuilderUtils.mapToRequiredInputType(idField), idField.description, idField) 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/schema/relay/RelayResolveOutput.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.schema.relay 2 | 3 | import cool.graph.DataItem 4 | import sangria.schema.Args 5 | 6 | case class RelayResolveOutput(clientMutationId: String, item: DataItem, args: Args) 7 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/server/HealthChecks.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.server 2 | 3 | import cool.graph.shared.database.GlobalDatabaseManager 4 | import slick.jdbc.MySQLProfile.api._ 5 | 6 | import scala.concurrent.{ExecutionContext, Future} 7 | 8 | object HealthChecks { 9 | def checkDatabases(globalDatabaseManager: GlobalDatabaseManager)(implicit ec: ExecutionContext): Future[Unit] = { 10 | Future 11 | .sequence { 12 | globalDatabaseManager.databases.values.map { db => 13 | for { 14 | _ <- db.master.run(sql"SELECT 1".as[Int]) 15 | _ <- db.readOnly.run(sql"SELECT 1".as[Int]) 16 | } yield () 17 | } 18 | } 19 | .map(_ => ()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/client/server/ProjectSchemaBuilder.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.client.server 2 | 3 | import cool.graph.client.UserContext 4 | import cool.graph.shared.models.Project 5 | import sangria.schema.Schema 6 | 7 | trait ProjectSchemaBuilder { 8 | def build(project: Project): Schema[UserContext, Unit] 9 | } 10 | 11 | object ProjectSchemaBuilder { 12 | def apply(fn: Project => Schema[UserContext, Unit]): ProjectSchemaBuilder = new ProjectSchemaBuilder { 13 | override def build(project: Project) = fn(project) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/private_api/mutations/PrivateMutation.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.private_api.mutations 2 | 3 | import cool.graph.Mutaction 4 | import cool.graph.shared.errors.GeneralError 5 | 6 | import scala.concurrent.{ExecutionContext, Future} 7 | 8 | trait PrivateMutation[T] { 9 | def execute()(implicit ec: ExecutionContext): Future[T] = { 10 | for { 11 | mutactions <- prepare 12 | results <- Future.sequence(mutactions.map(_.execute)) 13 | errors = results.collect { case e: GeneralError => e } 14 | } yield { 15 | if (errors.nonEmpty) { 16 | throw errors.head 17 | } else { 18 | result 19 | } 20 | } 21 | } 22 | 23 | def prepare: Future[List[Mutaction]] 24 | 25 | def result: T 26 | } 27 | -------------------------------------------------------------------------------- /server/client-shared/src/main/scala/cool/graph/webhook/Webhook.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.webhook 2 | 3 | import cool.graph.messagebus.Conversions 4 | import play.api.libs.json.{Json, Reads, Writes} 5 | 6 | object Webhook { 7 | implicit val mapStringReads = Reads.mapReads[String] 8 | implicit val mapStringWrites = Writes.mapWrites[String] 9 | implicit val webhooksWrites = Json.format[Webhook] 10 | implicit val marshaller = Conversions.Marshallers.FromJsonBackedType[Webhook]() 11 | implicit val unmarshaller = Conversions.Unmarshallers.ToJsonBackedType[Webhook]() 12 | } 13 | 14 | case class Webhook( 15 | projectId: String, 16 | functionId: String, 17 | requestId: String, 18 | url: String, 19 | payload: String, 20 | id: String, 21 | headers: Map[String, String] 22 | ) 23 | -------------------------------------------------------------------------------- /server/docker-compose/backend-dev.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | db: 4 | image: mysql:5.7 5 | command: mysqld --max-connections=1000 --sql-mode="ANSI,ALLOW_INVALID_DATES,ANSI_QUOTES,ERROR_FOR_DIVISION_BY_ZERO,HIGH_NOT_PRECEDENCE,IGNORE_SPACE,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_BACKSLASH_ESCAPES,NO_ENGINE_SUBSTITUTION,NO_KEY_OPTIONS,NO_UNSIGNED_SUBTRACTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY,PIPES_AS_CONCAT,REAL_AS_FLOAT,STRICT_ALL_TABLES,STRICT_TRANS_TABLES,TRADITIONAL" 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: $SQL_CLIENT_PASSWORD 9 | MYSQL_DATABASE: $SQL_INTERNAL_DATABASE 10 | ports: 11 | - "127.0.0.1:3306:3306" 12 | 13 | rabbit: 14 | image: rabbitmq:3-management 15 | restart: always 16 | hostname: rabbit-host 17 | ports: 18 | - "127.0.0.1:5672:5672" 19 | - "127.0.0.1:15672:15672" 20 | -------------------------------------------------------------------------------- /server/libs/akka-utils/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies ++= Seq( 2 | "com.typesafe.akka" %% "akka-actor" % "2.4.8" % "provided", 3 | "com.typesafe.akka" %% "akka-contrib" % "2.4.8" % "provided", 4 | "com.typesafe.akka" %% "akka-http" % "10.0.5", 5 | "com.typesafe.akka" %% "akka-testkit" % "2.4.8" % "test", 6 | "org.specs2" %% "specs2-core" % "3.8.8" % "test", 7 | "com.github.ben-manes.caffeine" % "caffeine" % "2.4.0", 8 | "com.twitter" %% "finagle-http" % "6.44.0" 9 | ) 10 | 11 | fork in Test := true 12 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/main/scala/cool/graph/akkautil/LogUnhandled.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil 2 | 3 | import akka.actor.Actor.Receive 4 | 5 | trait LogUnhandled { self => 6 | private val className = self.getClass.getSimpleName 7 | 8 | def logUnhandled(receive: Receive): Receive = receive orElse { 9 | case x => 10 | println(Console.RED + s"[$className] Received unknown message: $x" + Console.RESET) 11 | } 12 | 13 | def logAll(receive: Receive): Receive = { 14 | case x => 15 | if (receive.isDefinedAt(x)) { 16 | receive(x) 17 | println(Console.GREEN + s"[$className] Handled message: $x" + Console.RESET) 18 | } else { 19 | println(Console.RED + s"[$className] Unhandled message: $x" + Console.RESET) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/main/scala/cool/graph/akkautil/LogUnhandledExceptions.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil 2 | 3 | import akka.actor.Actor 4 | import cool.graph.bugsnag.{BugSnagger, MetaData} 5 | 6 | trait LogUnhandledExceptions extends Actor { 7 | 8 | val bugsnag: BugSnagger 9 | 10 | override def preRestart(reason: Throwable, message: Option[Any]): Unit = { 11 | super.preRestart(reason, message) 12 | bugsnag.report(reason, Seq(MetaData("Akka", "message", message))) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/main/scala/cool/graph/akkautil/SingleThreadedActorSystem.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil 2 | 3 | import java.util.concurrent.atomic.AtomicLong 4 | import java.util.concurrent.{Executors, ThreadFactory} 5 | 6 | import akka.actor.ActorSystem 7 | 8 | object SingleThreadedActorSystem { 9 | def apply(name: String): ActorSystem = { 10 | val ec = scala.concurrent.ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor(newNamedThreadFactory(name))) 11 | ActorSystem(name, defaultExecutionContext = Some(ec)) 12 | } 13 | 14 | def newNamedThreadFactory(name: String): ThreadFactory = new ThreadFactory { 15 | val count = new AtomicLong(0) 16 | 17 | override def newThread(runnable: Runnable): Thread = { 18 | val thread = new Thread(runnable) 19 | thread.setDaemon(true) 20 | thread.setName(s"$name-" + count.getAndIncrement) 21 | thread 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/main/scala/cool/graph/akkautil/http/Routes.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil.http 2 | 3 | object Routes { 4 | val emptyRoute = akka.http.scaladsl.server.Directives.reject 5 | } 6 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/main/scala/cool/graph/akkautil/http/Server.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil.http 2 | 3 | import akka.http.scaladsl.server.Route 4 | import akka.http.scaladsl.server.Directives.pathPrefix 5 | import scala.concurrent.Future 6 | 7 | trait Server { 8 | val prefix: String 9 | 10 | protected val innerRoutes: Route 11 | 12 | def routes: Route = prefix match { 13 | case prfx if prfx.nonEmpty => pathPrefix(prfx) { innerRoutes } 14 | case _ => innerRoutes 15 | } 16 | 17 | def onStart: Future[_] = Future.successful(()) 18 | def onStop: Future[_] = Future.successful(()) 19 | 20 | final def healthCheck: Future[_] = Future.successful(()) 21 | } 22 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/test/scala/cool/graph/akkautil/specs2/AcceptanceSpecification.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil.specs2 2 | 3 | import org.specs2.Specification 4 | import org.specs2.matcher.ThrownExpectations 5 | 6 | /** 7 | * This trait enables the usage of blocks for examples in the acceptance spec style. 8 | */ 9 | trait AcceptanceSpecification extends Specification with ThrownExpectations 10 | -------------------------------------------------------------------------------- /server/libs/akka-utils/src/test/scala/cool/graph/akkautil/specs2/AkkaTestKitSpecs2Context.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.akkautil.specs2 2 | 3 | import akka.actor.ActorSystem 4 | import akka.testkit.{ImplicitSender, TestKit} 5 | import com.typesafe.config.ConfigFactory 6 | import org.specs2.mutable.After 7 | 8 | import scala.concurrent.Await 9 | 10 | object TestConfig { 11 | val config = ConfigFactory.parseString(""" 12 | |akka { 13 | | log-dead-letters = 1 14 | |} 15 | | 16 | """.stripMargin) 17 | } 18 | 19 | /** 20 | * This class is a context for specs2, which allows the usage of the TestKit provided by Akka 21 | * to test actor systems. 22 | */ 23 | class AkkaTestKitSpecs2Context extends TestKit(ActorSystem("test-system", TestConfig.config)) with ImplicitSender with After { 24 | 25 | import scala.concurrent.duration._ 26 | 27 | def after = Await.result(system.terminate(), 10.seconds) 28 | } 29 | -------------------------------------------------------------------------------- /server/libs/aws/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies ++= Seq( 2 | "com.typesafe.akka" %% "akka-actor" % "2.4.8" % "provided", 3 | "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.4", 4 | "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.4", 5 | "com.fasterxml.jackson.core" % "jackson-core" % "2.8.4", 6 | "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % "2.8.4" 7 | ) 8 | -------------------------------------------------------------------------------- /server/libs/bugsnag/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies ++= Seq( 2 | "com.bugsnag" % "bugsnag" % "3.0.2", 3 | "org.specs2" %% "specs2-core" % "3.8.8" % "test", 4 | "com.typesafe.play" %% "play" % "2.4.0" % "test", 5 | "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.4", 6 | "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.4", 7 | "com.fasterxml.jackson.core" % "jackson-core" % "2.8.4", 8 | "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % "2.8.4" 9 | ) 10 | -------------------------------------------------------------------------------- /server/libs/cache/src/test/scala/cool/graph/cache/AwaitUtil.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.cache 2 | 3 | import scala.concurrent.{Await, Awaitable} 4 | 5 | trait AwaitUtil { 6 | import scala.concurrent.duration._ 7 | def await[T](awaitable: Awaitable[T]): T = Await.result(awaitable, 5.seconds) 8 | } 9 | -------------------------------------------------------------------------------- /server/libs/cache/src/test/scala/cool/graph/cache/CaffeineImplForAsyncCacheSpec.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.cache 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | import scala.concurrent.Future 6 | 7 | class CaffeineImplForAsyncCacheSpec extends FlatSpec with Matchers with AwaitUtil { 8 | import scala.concurrent.ExecutionContext.Implicits.global 9 | 10 | def newCache = CaffeineImplForAsyncCache.lfu[String, String](initialCapacity = 100, maxCapacity = 100) 11 | 12 | //this test is flaky 13 | 14 | "it" should "handle None results correctly" ignore { 15 | val cache = newCache 16 | val result = await(cache.getOrUpdateOpt("key", () => Future.successful(None))) 17 | result should be(None) 18 | 19 | val foo = Some("foo") 20 | val result2 = await(cache.getOrUpdateOpt("key", () => Future.successful(foo))) 21 | result2 should be(foo) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/libs/cache/src/test/scala/cool/graph/cache/CaffeineImplForSyncCacheSpec.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.cache 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | class CaffeineImplForSyncCacheSpec extends FlatSpec with Matchers { 6 | 7 | def newCache = CaffeineImplForCache.lfu[String, String](initialCapacity = 100, maxCapacity = 100) 8 | 9 | "it" should "handle None results correctly" in { 10 | val cache = newCache 11 | val result = cache.getOrUpdateOpt("key", () => None) 12 | result should be(None) 13 | cache.underlying.estimatedSize() should be(0) 14 | 15 | val foo = Some("foo") 16 | val result2 = cache.getOrUpdateOpt("key", () => foo) 17 | result2 should be(foo) 18 | cache.underlying.estimatedSize() should be(1) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/libs/graphql-client/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies ++= Seq( 2 | "com.typesafe.akka" %% "akka-http" % "10.0.5" % "provided", 3 | "com.typesafe.play" %% "play-json" % "2.5.12" 4 | ) 5 | 6 | fork in Test := true 7 | -------------------------------------------------------------------------------- /server/libs/javascript-engine/build.sbt: -------------------------------------------------------------------------------- 1 | libraryDependencies ++= Seq( 2 | "com.typesafe.akka" %% "akka-actor" % "2.4.8" % "provided", 3 | "org.specs2" %% "specs2-core" % "3.8.8" % "test", 4 | "com.typesafe" % "jse_2.11" % "1.2.0", 5 | "cool.graph" % "cuid-java" % "0.1.1", 6 | "org.scalatest" %% "scalatest" % "2.2.6" % "test" 7 | ) 8 | 9 | fork in Test := true 10 | -------------------------------------------------------------------------------- /server/libs/javascript-engine/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | blocking-process-io-dispatcher { 2 | type = Dispatcher 3 | executor = "thread-pool-executor" 4 | thread-pool-executor { 5 | core-pool-size-min = 3 6 | core-pool-size-factor = 1.0 7 | core-pool-size-max = 100 8 | } 9 | } -------------------------------------------------------------------------------- /server/libs/jvm-profiler/src/main/scala/cool/graph/profiling/JvmProfiler.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.profiling 2 | 3 | import akka.actor.Cancellable 4 | import cool.graph.metrics.MetricsManager 5 | 6 | import scala.concurrent.duration.FiniteDuration 7 | import scala.concurrent.duration._ 8 | 9 | object JvmProfiler { 10 | def schedule( 11 | metricsManager: MetricsManager, 12 | initialDelay: FiniteDuration = 0.seconds, 13 | interval: FiniteDuration = 5.seconds 14 | ): Cancellable = { 15 | import metricsManager.gaugeFlushSystem.dispatcher 16 | val memoryProfiler = MemoryProfiler(metricsManager) 17 | val cpuProfiler = CpuProfiler(metricsManager) 18 | metricsManager.gaugeFlushSystem.scheduler.schedule(initialDelay, interval) { 19 | memoryProfiler.profile() 20 | cpuProfiler.profile() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/libs/message-bus/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "cool.graph" 2 | name := "message-bus" 3 | scalaVersion := "2.11.8" 4 | 5 | 6 | libraryDependencies ++= Seq( 7 | "com.typesafe.akka" %% "akka-actor" % "2.4.8" % "provided", 8 | "com.typesafe.akka" %% "akka-testkit" % "2.4.8" % "test", 9 | "org.specs2" %% "specs2-core" % "3.8.8" % "test", 10 | "com.typesafe.akka" %% "akka-cluster-tools" % "2.4.17" 11 | ) 12 | -------------------------------------------------------------------------------- /server/libs/message-bus/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /server/libs/message-bus/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loglevel = INFO 3 | actor.provider = "akka.cluster.ClusterActorRefProvider" 4 | loglevel = WARNING 5 | remote { 6 | log-remote-lifecycle-events = off 7 | netty.tcp { 8 | hostname = "127.0.0.1" 9 | port = 0 10 | port = ${?AKKA_CLUSTER_PORT} 11 | } 12 | 13 | default-remote-dispatcher { 14 | type = Dispatcher 15 | executor = "fork-join-executor" 16 | fork-join-executor { 17 | parallelism-min = 1 18 | parallelism-factor = 0.5 19 | parallelism-max = 1 20 | } 21 | throughput = 10 22 | } 23 | } 24 | 25 | test { 26 | single-expect-default = 6s 27 | } 28 | 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /server/libs/message-bus/src/main/scala/cool/graph/messagebus/pubsub/rabbit/RabbitAkkaPubSubPublisher.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.messagebus.pubsub.rabbit 2 | 3 | import cool.graph.messagebus.Conversions.ByteMarshaller 4 | import cool.graph.messagebus.pubsub.Only 5 | import cool.graph.messagebus.PubSubPublisher 6 | import cool.graph.rabbit.Import.Exchange 7 | 8 | case class RabbitAkkaPubSubPublisher[T]( 9 | exchange: Exchange, 10 | onShutdown: () => Unit = () => () 11 | )(implicit marshaller: ByteMarshaller[T]) 12 | extends PubSubPublisher[T] { 13 | def publish(topic: Only, msg: T): Unit = exchange.publish(topic.topic, marshaller(msg)) 14 | override def shutdown: Unit = onShutdown() 15 | } 16 | -------------------------------------------------------------------------------- /server/libs/message-bus/src/main/scala/cool/graph/messagebus/queue/rabbit/MessageInfo.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.messagebus.queue.rabbit 2 | 3 | case class MessageInfo(tries: Int, tryNextAt: Option[Long]) { 4 | def isDelayed: Boolean = { 5 | tryNextAt match { 6 | case Some(processingTime) => 7 | val now = System.currentTimeMillis / 1000 8 | now < processingTime 9 | 10 | case None => 11 | false 12 | } 13 | } 14 | 15 | // Messages start at 0 and have 5 tries. Tries are incremented after each unsuccessful processing. 16 | def exceededTries: Boolean = tries >= RabbitQueueConsumer.MAX_TRIES 17 | } 18 | -------------------------------------------------------------------------------- /server/libs/message-bus/src/main/scala/cool/graph/messagebus/utils/RabbitUtils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.messagebus.utils 2 | 3 | import cool.graph.bugsnag.BugSnagger 4 | import cool.graph.rabbit.Import.{Exchange, Rabbit} 5 | 6 | import scala.util.{Failure, Success} 7 | 8 | object RabbitUtils { 9 | def declareExchange(amqpUri: String, exchangeName: String, concurrency: Int, durable: Boolean)(implicit bugSnagger: BugSnagger): Exchange = { 10 | val exchangeTry = for { 11 | channel <- Rabbit.channel(exchangeName, amqpUri, consumerThreads = concurrency) 12 | exDecl <- channel.exchangeDeclare(s"$exchangeName-exchange", durable = durable) 13 | } yield exDecl 14 | 15 | exchangeTry match { 16 | case Success(ex) => ex 17 | case Failure(err) => 18 | throw new Exception(s"Unable to declare rabbit exchange: $err", err) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/libs/message-bus/src/main/scala/cool/graph/messagebus/utils/Utils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.messagebus.utils 2 | 3 | import scala.io.Source 4 | import scala.util.{Failure, Success, Try} 5 | 6 | object Utils { 7 | 8 | val dockerContainerID: String = { 9 | Try { 10 | val source = Source.fromFile("/etc/hostname") 11 | val hostname = try { source.mkString.trim } finally source.close() 12 | 13 | hostname 14 | } match { 15 | case Success(hostname) => hostname 16 | case Failure(err) => println("Warning: Unable to read hostname from /etc/hostname"); "" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/libs/metrics/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /server/libs/metrics/src/main/scala/cool/graph/metrics/ContainerMetadata.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.metrics 2 | 3 | import scala.io.Source 4 | 5 | object ContainerMetadata { 6 | 7 | /** 8 | * Fetches the docker container ID of the current container using the hostsname file. 9 | * 10 | * @return The docker container ID of the container running this service. 11 | */ 12 | def fetchContainerId(): String = { 13 | val source = Source.fromFile("/etc/hostname") 14 | val hostname = try source.mkString.trim 15 | finally source.close() 16 | 17 | hostname 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/libs/metrics/src/main/scala/cool/graph/metrics/Utils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.metrics 2 | 3 | import com.twitter.util.{Return, Throw, Future => TwitterFuture, Promise => TwitterPromise} 4 | import scala.concurrent.{ExecutionContext, Future => ScalaFuture, Promise => ScalaPromise} 5 | 6 | object Utils { 7 | 8 | implicit class RichTwitterFuture[A](val tf: TwitterFuture[A]) extends AnyVal { 9 | def asScala(implicit e: ExecutionContext): ScalaFuture[A] = { 10 | val promise: ScalaPromise[A] = ScalaPromise() 11 | tf.respond { 12 | case Return(value) => promise.success(value) 13 | case Throw(exception) => promise.failure(exception) 14 | } 15 | promise.future 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/libs/metrics/src/test/scala/cool/graph/metrics/utils/TestLiveMetricsManager.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.metrics.utils 2 | 3 | import com.timgroup.statsd.{NonBlockingStatsDClient, StatsDClient} 4 | import cool.graph.metrics.MetricsManager 5 | 6 | class TestLiveMetricsManager extends MetricsManager { 7 | def serviceName: String = "TestService" 8 | 9 | override val baseTagsString: String = "env=test,instance=local,container=none" 10 | override val client: StatsDClient = new NonBlockingStatsDClient(serviceName, "127.0.0.1", 8125, new Array[String](0), errorHandler) 11 | } 12 | -------------------------------------------------------------------------------- /server/libs/metrics/src/test/scala/cool/graph/metrics/utils/TestMetricsManager.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.metrics.utils 2 | 3 | import com.timgroup.statsd.StatsDClient 4 | import cool.graph.metrics.{DummyStatsDClient, MetricsManager} 5 | 6 | class TestMetricsManager extends MetricsManager { 7 | def serviceName: String = "TestService" 8 | 9 | override val baseTagsString: String = "env=test,instance=local,container=none" 10 | override val client: StatsDClient = new DummyStatsDClient 11 | } 12 | -------------------------------------------------------------------------------- /server/libs/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/.gitignore: -------------------------------------------------------------------------------- 1 | /RUNNING_PID 2 | /logs/ 3 | /project/*-shim.sbt 4 | /project/project/ 5 | /project/target/ 6 | /target/ 7 | .idea 8 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "cool.graph" 2 | name := "rabbit-processor" 3 | 4 | libraryDependencies ++= Seq( 5 | "com.rabbitmq" % "amqp-client" % "4.1.0", 6 | "com.fasterxml.jackson.core" % "jackson-databind" % "2.8.4", 7 | "com.fasterxml.jackson.core" % "jackson-annotations" % "2.8.4", 8 | "com.fasterxml.jackson.core" % "jackson-core" % "2.8.4", 9 | "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % "2.8.4" 10 | ) 11 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Tue Jul 28 08:26:26 CEST 2015 3 | template.uuid=e17acfbb-1ff5-41f5-b8cf-2c40be6a8340 4 | sbt.version=0.13.8 5 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/release-notes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 0.1.0 4 | * initial release 5 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/src/main/scala/cool/graph/rabbit/Import.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.rabbit 2 | 3 | object Import { 4 | val Rabbit = cool.graph.rabbit.Rabbit 5 | type Channel = cool.graph.rabbit.Channel 6 | val Channel = cool.graph.rabbit.Channel 7 | type Queue = cool.graph.rabbit.Queue 8 | val Queue = cool.graph.rabbit.Queue 9 | type Exchange = cool.graph.rabbit.Exchange 10 | val Exchange = cool.graph.rabbit.Exchange 11 | type Consumer = cool.graph.rabbit.Consumer 12 | val Consumer = cool.graph.rabbit.Consumer 13 | 14 | val ExchangeTypes = cool.graph.rabbit.ExchangeTypes 15 | val Bindings = cool.graph.rabbit.Bindings 16 | } 17 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/src/main/scala/cool/graph/rabbit/Utils.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.rabbit 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.{Date, UUID} 5 | import java.util.concurrent.ThreadFactory 6 | import java.util.concurrent.atomic.AtomicLong 7 | 8 | object Utils { 9 | def timestamp: String = { 10 | val formatter = new SimpleDateFormat("HH:mm:ss.SSS-dd.MM.yyyy") 11 | val now = new Date() 12 | formatter.format(now) 13 | } 14 | 15 | def timestampWithRandom: String = timestamp + "-" + UUID.randomUUID() 16 | 17 | def newNamedThreadFactory(name: String): ThreadFactory = new ThreadFactory { 18 | val count = new AtomicLong(0) 19 | 20 | override def newThread(runnable: Runnable): Thread = { 21 | val thread = new Thread(runnable) 22 | thread.setName(s"$name-" + count.getAndIncrement) 23 | thread.setDaemon(true) 24 | thread 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/libs/rabbit-processor/version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "0.1.0-SNAPSHOT" -------------------------------------------------------------------------------- /server/libs/scala-utils/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /server/libs/scala-utils/src/main/scala/cool/graph/utils/try/TryExtensions.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.utils.`try` 2 | 3 | import scala.concurrent.Future 4 | import scala.util.{Failure, Success, Try} 5 | 6 | object TryExtensions { 7 | implicit class TryExtensions[T](val theTry: Try[T]) extends AnyVal { 8 | def toFuture: Future[T] = theTry match { 9 | case Success(value) => Future.successful(value) 10 | case Failure(exception) => Future.failed(exception) 11 | } 12 | } 13 | } 14 | 15 | object TryUtil { 16 | def sequence[T](trys: Vector[Try[T]]): Try[Vector[T]] = { 17 | val successes = trys.collect { case Success(x) => x } 18 | val failures = trys.collect { case f @ Failure(_) => f } 19 | if (successes.length == trys.length) { 20 | Success(successes) 21 | } else { 22 | failures.head.asInstanceOf[Try[Vector[T]]] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/libs/stub-server/.gitignore: -------------------------------------------------------------------------------- 1 | /RUNNING_PID 2 | /logs/ 3 | /project/*-shim.sbt 4 | /project/project/ 5 | /project/target/ 6 | /target/ 7 | .idea 8 | -------------------------------------------------------------------------------- /server/libs/stub-server/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "cool.graph" 2 | name := """stub-server""" 3 | 4 | scalaVersion := "2.11.6" 5 | 6 | // Change this to another test framework if you prefer 7 | libraryDependencies ++= Seq( 8 | "org.eclipse.jetty" % "jetty-server" % "9.3.0.v20150612", 9 | "com.netaporter" %% "scala-uri" % "0.4.16", 10 | "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4", 11 | "org.scalaj" %% "scalaj-http" % "1.1.4" % "test", 12 | "org.scalatest" %% "scalatest" % "2.2.4" % "test", 13 | "org.specs2" %% "specs2-core" % "3.6.1" % "test" 14 | ) 15 | 16 | resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases" 17 | 18 | parallelExecution in Test := false 19 | 20 | scalacOptions in Test ++= Seq("-Yrangepos") 21 | -------------------------------------------------------------------------------- /server/libs/stub-server/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Wed Jun 17 10:54:41 CEST 2015 3 | template.uuid=7faf8e1e-4e8d-4387-8159-642b50383096 4 | sbt.version=0.13.13 5 | -------------------------------------------------------------------------------- /server/libs/stub-server/release-notes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## 0.1.0 4 | * initial release 5 | -------------------------------------------------------------------------------- /server/libs/stub-server/src/main/scala/cool/graph/stub/QueryString.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.stub 2 | 3 | import com.netaporter.uri.Uri 4 | 5 | import scala.collection.SortedMap 6 | 7 | object QueryString { 8 | def queryStringToMap(asNullableString: String): Map[String, String] = { 9 | Option(asNullableString) match { 10 | case Some(string) => 11 | Map(Uri.parse(s"?$string").query.params: _*).filterKeys(_.trim != "").mapValues(_.getOrElse("")) 12 | case None => 13 | Map.empty 14 | } 15 | 16 | } 17 | 18 | def queryMapToString(queryMap: Map[String, Any]): String = { 19 | queryMap.isEmpty match { 20 | case false => "?" + queryMap.map { case (k, v) => s"$k=$v" }.mkString("&") 21 | case true => "" 22 | } 23 | } 24 | 25 | def mapToSortedMap(map: Map[String, Any]): SortedMap[String, Any] = { 26 | SortedMap(map.toSeq: _*) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/libs/stub-server/src/test/scala/cool/graph/stub/StubDslSpec.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.stub 2 | 3 | import cool.graph.stub.StubDsl.Default.Request 4 | import org.specs2.mutable.Specification 5 | 6 | class StubDslSpec extends Specification { 7 | 8 | val path = "some/freaking/path" 9 | val params = Map("a" -> 1) 10 | 11 | val responseBody = "the body" 12 | val response = StaticStubResponse(333, responseBody) 13 | 14 | "using the default stub DSL" should { 15 | "produce a stub response with headers" in { 16 | val stub: Stub = Request("POST", path).stub(200, responseBody, Map("X-Test" -> "Test")) 17 | stub.stubbedResponse.headers("X-Test") must equalTo("Test") 18 | stub.stubbedResponse.body must equalTo(responseBody) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/libs/stub-server/version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "0.1.0-SNAPSHOT" -------------------------------------------------------------------------------- /server/localfaas/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /server/localfaas/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | daemonic = on 3 | loglevel = INFO 4 | http.server { 5 | parsing.max-uri-length = 50k 6 | parsing.max-header-value-length = 50k 7 | request-timeout = 120s // Deploy mutation is too slow for default 20s 8 | } 9 | http.host-connection-pool { 10 | // see http://doc.akka.io/docs/akka-http/current/scala/http/client-side/pool-overflow.html 11 | // and http://doc.akka.io/docs/akka-http/current/java/http/configuration.html 12 | // These settings are relevant for Region Proxy Synchronous Request Pipeline functions and ProjectSchemaFetcher 13 | max-connections = 64 // default is 4, but we have multiple servers behind lb, so need many connections to single host 14 | max-open-requests = 2048 // default is 32, but we need to handle spikes 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/localfaas/src/main/scala/cool/graph/localfaas/LocalFaasMain.scala: -------------------------------------------------------------------------------- 1 | package cool.graph.localfaas 2 | 3 | import akka.actor.ActorSystem 4 | import akka.stream.ActorMaterializer 5 | import better.files.File.root 6 | import cool.graph.akkautil.http.ServerExecutor 7 | 8 | object LocalFaasMain extends App { 9 | implicit val system = ActorSystem("functions-runtime") 10 | implicit val materializer = ActorMaterializer() 11 | 12 | val port = sys.env.getOrElse("FUNCTIONS_PORT", sys.error("FUNCTIONS_PORT env var required but not found.")).toInt 13 | val workingDir = (root / "var" / "faas").createIfNotExists(asDirectory = true, createParents = true) 14 | 15 | val executor = ServerExecutor( 16 | port = port, 17 | FunctionRuntimeServer("functions", workingDir) 18 | ).startBlocking() 19 | } 20 | -------------------------------------------------------------------------------- /server/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.16 2 | -------------------------------------------------------------------------------- /server/project/libs/maven-packagecloud-wagon-idempotent.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Graphcool/graphcool-framework/ebecea1fbf870999a744d72193a6b391dddc0666/server/project/libs/maven-packagecloud-wagon-idempotent.jar -------------------------------------------------------------------------------- /server/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | unmanagedBase := baseDirectory.value / "libs" 2 | 3 | libraryDependencies ++= Seq( 4 | "org.scalaj" %% "scalaj-http" % "2.3.0", 5 | "com.typesafe.play" %% "play-json" % "2.6.6" 6 | ) 7 | 8 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 9 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.3") 10 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.1") 11 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") 12 | 13 | addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC12") 14 | addSbtPlugin("org.duhemm" % "sbt-errors-summary" % "0.4.0") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.9.3") 17 | 18 | addSbtPlugin("no.arktekk.sbt" % "aether-deploy" % "0.17") 19 | -------------------------------------------------------------------------------- /server/scripts/kill-all-docker-containers.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | docker kill $(docker ps -q) &> /dev/null 4 | echo "Stopped all old docker containers..." -------------------------------------------------------------------------------- /server/scripts/publish-jars.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | docker run -e "BRANCH=${BUILDKITE_BRANCH}" -e "PACKAGECLOUD_PW=${PACKAGECLOUD_PW}" -e "GITHUB_ACCESS_TOKEN=${GITHUB_ACCESS_TOKEN}" -e "OTHER_REPO_OWNER=${OTHER_REPO_OWNER}" -e "OTHER_REPO=${OTHER_REPO}" -e "OTHER_REPO_FILE=${OTHER_REPO_FILE}" -v $(pwd):/root/build -w /root/build/server -v ~/.ivy2:/root/.ivy2 -v ~/.coursier:/root/.coursier -v /var/run/docker.sock:/var/run/docker.sock schickling/scala-sbt-docker sbt publish propagateVersionToOtherRepo 6 | -------------------------------------------------------------------------------- /server/single-server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------