potassium 6.0.0 → 6.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +103 -38
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.gitignore +2 -1
  5. data/.rubocop.yml +530 -0
  6. data/CHANGELOG.md +57 -0
  7. data/README.md +51 -45
  8. data/lib/potassium/assets/.circleci/config.yml.erb +83 -34
  9. data/lib/potassium/assets/.eslintrc.json +13 -4
  10. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  11. data/lib/potassium/assets/.rubocop.yml +13 -0
  12. data/lib/potassium/assets/README.yml +7 -7
  13. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  14. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  15. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  16. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  17. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  18. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  19. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  20. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  21. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  22. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  23. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  24. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  25. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  26. data/lib/potassium/assets/app/javascript/app.spec.js +1 -1
  27. data/lib/potassium/assets/app/uploaders/base_uploader.rb +1 -3
  28. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  29. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  30. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  31. data/lib/potassium/assets/config/puma.rb +1 -1
  32. data/lib/potassium/assets/config/shrine.rb +4 -1
  33. data/lib/potassium/assets/redis.yml +1 -2
  34. data/lib/potassium/assets/testing/rails_helper.rb +2 -0
  35. data/lib/potassium/cli/commands/create.rb +11 -19
  36. data/lib/potassium/cli_options.rb +70 -10
  37. data/lib/potassium/helpers/template-helpers.rb +4 -0
  38. data/lib/potassium/newest_version_ensurer.rb +19 -36
  39. data/lib/potassium/node_version_ensurer.rb +30 -0
  40. data/lib/potassium/recipes/admin.rb +26 -16
  41. data/lib/potassium/recipes/api.rb +92 -27
  42. data/lib/potassium/recipes/background_processor.rb +62 -18
  43. data/lib/potassium/recipes/ci.rb +9 -39
  44. data/lib/potassium/recipes/database.rb +4 -0
  45. data/lib/potassium/recipes/draper.rb +0 -9
  46. data/lib/potassium/recipes/file_storage.rb +2 -1
  47. data/lib/potassium/recipes/front_end.rb +84 -9
  48. data/lib/potassium/recipes/github.rb +93 -15
  49. data/lib/potassium/recipes/google_tag_manager.rb +94 -0
  50. data/lib/potassium/recipes/heroku.rb +42 -29
  51. data/lib/potassium/recipes/mailer.rb +18 -5
  52. data/lib/potassium/recipes/monitoring.rb +5 -0
  53. data/lib/potassium/recipes/schedule.rb +16 -1
  54. data/lib/potassium/recipes/style.rb +2 -2
  55. data/lib/potassium/templates/application.rb +5 -2
  56. data/lib/potassium/version.rb +5 -2
  57. data/potassium.gemspec +5 -2
  58. data/spec/features/api_spec.rb +25 -0
  59. data/spec/features/background_processor_spec.rb +19 -6
  60. data/spec/features/ci_spec.rb +7 -4
  61. data/spec/features/draper_spec.rb +1 -6
  62. data/spec/features/file_storage_spec.rb +5 -0
  63. data/spec/features/front_end_spec.rb +32 -1
  64. data/spec/features/github_spec.rb +53 -8
  65. data/spec/features/google_tag_manager_spec.rb +36 -0
  66. data/spec/features/graphql_spec.rb +71 -0
  67. data/spec/features/mailer_spec.rb +16 -0
  68. data/spec/features/schedule_spec.rb +11 -4
  69. data/spec/spec_helper.rb +1 -0
  70. data/spec/support/fake_octokit.rb +31 -0
  71. data/spec/support/potassium_test_helpers.rb +0 -1
  72. data/tmp/.keep +0 -0
  73. metadata +80 -15
  74. data/lib/potassium/assets/Dockerfile.ci +0 -6
  75. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  76. data/lib/potassium/assets/api/base_controller.rb +0 -7
  77. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  78. data/lib/potassium/assets/api/responder.rb +0 -41
  79. data/lib/potassium/assets/bin/cibuild.erb +0 -117
  80. data/lib/potassium/assets/docker-compose.ci.yml +0 -12
  81. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
@@ -0,0 +1,55 @@
1
+ class GraphqlController < ApplicationController
2
+ # If accessing from outside this domain, nullify the session
3
+ # This allows for outside API access while preventing CSRF attacks,
4
+ # but you'll have to authenticate your user separately
5
+ # protect_from_forgery with: :null_session
6
+
7
+ def execute
8
+ variables = prepare_variables(params[:variables])
9
+ query = params[:query]
10
+ operation_name = params[:operationName]
11
+ context = { current_user: get_current_user }
12
+ result = GqlSampleSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
13
+ render json: result
14
+ rescue => e
15
+ raise e unless Rails.env.development?
16
+ handle_error_in_development e
17
+ end
18
+
19
+ private
20
+
21
+ # Handle variables in form data, JSON body, or a blank value
22
+ def prepare_variables(variables_param)
23
+ case variables_param
24
+ when String
25
+ if variables_param.present?
26
+ JSON.parse(variables_param) || {}
27
+ else
28
+ {}
29
+ end
30
+ when Hash
31
+ variables_param
32
+ when ActionController::Parameters
33
+ variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
34
+ when nil
35
+ {}
36
+ else
37
+ raise ArgumentError, "Unexpected parameter: #{variables_param}"
38
+ end
39
+ end
40
+
41
+ def handle_error_in_development(e)
42
+ logger.error e.message
43
+ logger.error e.backtrace.join("\n")
44
+
45
+ render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
46
+ end
47
+
48
+ def get_current_user
49
+ if request.headers['Authorization']
50
+ _, token = request.headers['Authorization'].split
51
+ decoded_token = JWT.decode token, ENV['HMAC_SECRET'], true, { algorithm: 'HS256' }
52
+ User.find(decoded_token.first["id"])
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,23 @@
1
+ require 'jwt'
2
+
3
+ class Mutations::LoginMutation < Mutations::BaseMutation
4
+ null true
5
+
6
+ argument :email, String, required: true
7
+ argument :password, String, required: true
8
+
9
+
10
+ field :token, String, null: true
11
+
12
+ def resolve(email:, password:)
13
+ user = User.find_by(email: email)
14
+ if user&.valid_password?(password)
15
+ payload = { id: user.id, email: user.email, exp: (Time.zone.now + 24.hours).to_i }
16
+ token = JWT.encode payload, ENV['HMAC_SECRET'], 'HS256'
17
+ return { token: token }
18
+ end
19
+ GraphQL::ExecutionError.new("User or Password invalid")
20
+ rescue ActiveRecord::RecordInvalid => e
21
+ GraphQL::ExecutionError.new("Invalid input: #{e.record.errors.full_messages.join(', ')}")
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ module Queries
2
+ class BaseQuery < GraphQL::Schema::Resolver
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types::Base
2
+ class BaseArgument < GraphQL::Schema::Argument
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types::Base
2
+ class BaseEnum < GraphQL::Schema::Enum
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Types::Base
2
+ class BaseField < GraphQL::Schema::Field
3
+ argument_class Types::Base::BaseArgument
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Types::Base
2
+ class BaseInputObject < GraphQL::Schema::InputObject
3
+ argument_class Types::Base::BaseArgument
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Types::Base
2
+ module BaseInterface
3
+ include GraphQL::Schema::Interface
4
+
5
+ field_class Types::Base::BaseField
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Types::Base
2
+ class BaseObject < GraphQL::Schema::Object
3
+ field_class Types::Base::BaseField
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Types::Base
2
+ class BaseScalar < GraphQL::Schema::Scalar
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types::Base
2
+ class BaseUnion < GraphQL::Schema::Union
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module Types
2
+ class MutationType < Types::Base::BaseObject
3
+ # TODO: remove me
4
+ field :test_field, String, null: false,
5
+ description: "An example field added by the generator"
6
+ def test_field
7
+ "Hello World"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Types
2
+ class QueryType < Types::Base::BaseObject
3
+ # Add root-level fields here.
4
+ # They will be entry points for queries on your schema.
5
+
6
+ # TODO: remove me
7
+ field :test_field, String, null: false,
8
+ description: "An example field added by the generator"
9
+ def test_field
10
+ "Hello World!"
11
+ end
12
+ end
13
+ end
@@ -4,7 +4,7 @@ import App from 'app';
4
4
  describe('App', () => {
5
5
  test('is a Vue instance', () => {
6
6
  const wrapper = shallowMount(App);
7
- expect(wrapper.isVueInstance()).toBeTruthy();
7
+ expect(wrapper.vm).toBeTruthy();
8
8
  });
9
9
 
10
10
  it('displays message on load', () => {
@@ -2,11 +2,9 @@ class BaseUploader < Shrine
2
2
  plugin :validation_helpers
3
3
 
4
4
  Attacher.validate do
5
- validate_mime_type allowed_types
5
+ validate_mime_type store.allowed_types
6
6
  end
7
7
 
8
- private
9
-
10
8
  def allowed_types
11
9
  raise NotImplementedError
12
10
  end
@@ -0,0 +1,4 @@
1
+ <!-- Google Tag Manager (noscript) -->
2
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%= ENV['GTM_CONTAINER_ID'] %>"
3
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
4
+ <!-- End Google Tag Manager (noscript) -->
@@ -0,0 +1,7 @@
1
+ <!-- Google Tag Manager -->
2
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
3
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
4
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
5
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
6
+ })(window,document,'script','dataLayer','<%=ENV['GTM_CONTAINER_ID']%>');</script>
7
+ <!-- End Google Tag Manager -->
@@ -0,0 +1,20 @@
1
+ # config/initializers/graphql_playground.rb
2
+ # All config options have a default that should work out of the box
3
+ if Rails.env.development?
4
+ GraphqlPlayground::Rails.configure do |config|
5
+ # config.headers = {
6
+ # 'X-Auth-Header' => ->(view_context) { "123" }
7
+ # }
8
+ # config.title = "Playground"
9
+ # config.csrf = true
10
+ # config.playground_version = "latest"
11
+ # # Ideally the assets would be added to your projects `vendor/assets` directories
12
+ # config.favicon = "/assets/playground.ico"
13
+ # config.playground_js_url = "/assets/playground.js"
14
+ # config.playground_css_url = "/assets/playground.css"
15
+ # # see: https://github.com/prisma-labs/graphql-playground#settings
16
+ config.settings = {
17
+ "schema.polling.enable": false
18
+ }
19
+ end
20
+ end
@@ -24,7 +24,7 @@ rack_env = ENV.fetch("RACK_ENV", "development")
24
24
  environment rack_env
25
25
 
26
26
  # Set 1 day timeout for workers while developing
27
- worker_timeout 1.day.seconds.to_i if rack_env == "development"
27
+ worker_timeout 24 * 60 * 60 if rack_env == "development"
28
28
 
29
29
  on_worker_boot do
30
30
  # Worker specific setup for Rails 4.1+
@@ -24,7 +24,10 @@ elsif Rails.env.production?
24
24
  else
25
25
  require 'shrine/storage/memory'
26
26
 
27
- Shrine.storages[:store] = Shrine::Storage::Memory.new
27
+ Shrine.storages = {
28
+ cache: Shrine::Storage::Memory.new,
29
+ store: Shrine::Storage::Memory.new
30
+ }
28
31
  end
29
32
 
30
33
  Shrine.plugin :activerecord
@@ -1,6 +1,5 @@
1
1
  development: &default
2
- host: <%= ENV["BOXEN_REDIS_HOST"] || ENV["REDIS_HOST"] || "127.0.0.1" %>
3
- port: <%= ENV["BOXEN_REDIS_PORT"] || ENV["REDIS_PORT"] || 6379 %>
2
+ url: <%= ENV.fetch("REDIS_URL") %>
4
3
 
5
4
  test:
6
5
  <<: *default
@@ -26,6 +26,8 @@ Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
26
26
  # If you are not using ActiveRecord, you can remove this line.
27
27
  ActiveRecord::Migration.maintain_test_schema!
28
28
 
29
+ PowerTypes::Observable.observable_disabled = true
30
+
29
31
  RSpec.configure do |config|
30
32
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
31
33
  # examples within a transaction, remove the following line or assign false
@@ -3,37 +3,29 @@ require 'potassium/cli_options'
3
3
  module Potassium::CLI
4
4
  extend Potassium::CliOptions
5
5
 
6
- desc "Create a new Potassium Rails project."
6
+ desc 'Create a new Potassium Rails project.'
7
7
  arg 'app_path'
8
8
  command :create do |c|
9
- c.default_desc "Create a new project."
10
- c.switch "version-check",
11
- default_value: true,
12
- desc: "Performs a version check before running.",
13
- negatable: true
14
-
9
+ c.default_desc 'Create a new project.'
15
10
  create_options.each { |opts| c.send(opts.delete(:type), opts.delete(:name), opts) }
16
11
 
17
12
  c.action do |_global_options, options, _args|
18
- require "potassium/newest_version_ensurer"
19
-
20
- begin_creation = -> do
21
- require "potassium/generators/application"
22
- require "potassium/template_finder"
13
+ require 'potassium/newest_version_ensurer'
14
+ require 'potassium/node_version_ensurer'
15
+ require 'potassium/generators/application'
16
+ require 'potassium/template_finder'
23
17
 
18
+ begin
19
+ Potassium::NewestVersionEnsurer.new.ensure! if options['version-check']
20
+ Potassium::NodeVersionEnsurer.new.ensure! if options['node-version-check']
24
21
  template_finder = Potassium::TemplateFinder.new
25
22
  template = template_finder.default_template
26
23
  template.cli_options = options
27
24
  template.source_paths << Rails::Generators::AppGenerator.source_root
28
25
  ARGV.push('--skip-webpack-install', '--skip-bundle')
29
26
  template.start
30
- end
31
-
32
- if options["version-check"]
33
- ensurer = Potassium::NewestVersionEnsurer.new
34
- ensurer.ensure(&begin_creation)
35
- else
36
- begin_creation.call
27
+ rescue VersionError => e
28
+ print "\nError: #{e.message}" # rubocop:disable Rails/Output
37
29
  end
38
30
  end
39
31
  end
@@ -1,5 +1,21 @@
1
1
  module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
2
2
  CREATE_OPTIONS = [
3
+ {
4
+ type: :switch,
5
+ name: 'version-check',
6
+ desc: 'Performs a version check before running.',
7
+ negatable: true,
8
+ default_value: true,
9
+ default_test_value: true
10
+ },
11
+ {
12
+ type: :switch,
13
+ name: 'node-version-check',
14
+ desc: 'Performs a node version check before running.',
15
+ negatable: true,
16
+ default_value: true,
17
+ default_test_value: true
18
+ },
3
19
  {
4
20
  type: :flag,
5
21
  name: [:db, :database],
@@ -59,12 +75,11 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
59
75
  default_test_value: false
60
76
  },
61
77
  {
62
- type: :switch,
78
+ type: :flag,
63
79
  name: "api",
64
- desc: "Whether to apply the API mode or not",
65
- negatable: true,
80
+ desc: "Which API interface to use",
66
81
  default_value: "none",
67
- default_test_value: false
82
+ default_test_value: "None"
68
83
  },
69
84
  {
70
85
  type: :flag,
@@ -82,10 +97,10 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
82
97
  default_test_value: false
83
98
  },
84
99
  {
85
- type: :flag,
100
+ type: :switch,
86
101
  name: "background_processor",
87
- desc: "Decides which background processor to use. Available: sidekiq, delayed_job, none",
88
- default_test_value: "None"
102
+ desc: "Whether to use Sidekiq for background processing or not",
103
+ default_test_value: false
89
104
  },
90
105
  {
91
106
  type: :switch,
@@ -105,12 +120,41 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
105
120
  },
106
121
  {
107
122
  type: :switch,
108
- name: "github-private",
109
- desc: "Whether to the github repository is private",
123
+ name: "github_private",
124
+ desc: "Whether the github repository is private or not",
110
125
  negatable: true,
111
- default_value: false,
126
+ default_value: "none",
112
127
  default_test_value: false
113
128
  },
129
+ {
130
+ type: :switch,
131
+ name: "github_has_org",
132
+ desc: "Whether the github repository should belong to an organization",
133
+ negatable: true,
134
+ default_value: "none",
135
+ default_test_value: false
136
+ },
137
+ {
138
+ type: :flag,
139
+ name: "github_org",
140
+ desc: "The github organization where the repository will be created",
141
+ default_value: "none",
142
+ default_test_value: "none"
143
+ },
144
+ {
145
+ type: :flag,
146
+ name: "github_name",
147
+ desc: "The github repository name",
148
+ default_value: "none",
149
+ default_test_value: "none"
150
+ },
151
+ {
152
+ type: :flag,
153
+ name: "github_access_token",
154
+ desc: "Github personal access token used to auth to Github API",
155
+ default_value: "none",
156
+ default_test_value: "none"
157
+ },
114
158
  {
115
159
  type: :switch,
116
160
  name: "schedule",
@@ -132,6 +176,22 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
132
176
  name: :front_end,
133
177
  desc: "Decides which front-end framework to use. Available: Vue, Angular 2, None",
134
178
  default_test_value: "None"
179
+ },
180
+ {
181
+ type: :switch,
182
+ name: 'google_tag_manager',
183
+ desc: 'Whether to use google tag manager',
184
+ negatable: true,
185
+ default_value: 'none',
186
+ default_test_value: false
187
+ },
188
+ {
189
+ type: :switch,
190
+ name: "test",
191
+ desc: "Whether or not it is a test project creation",
192
+ negatable: true,
193
+ default_value: false,
194
+ default_test_value: true
135
195
  }
136
196
  ]
137
197
 
@@ -7,6 +7,10 @@ module TemplateHelpers
7
7
  "#{Potassium::NODE_VERSION}.x"
8
8
  end
9
9
 
10
+ def ruby_version
11
+ Semantic::Version.new(Potassium::RUBY_VERSION).instance_eval { "#{major}.#{minor}" }
12
+ end
13
+
10
14
  def load_recipe(recipe_name)
11
15
  @recipes ||= {}
12
16
  @recipes[recipe_name] ||= get_recipe_class(recipe_name.to_sym).new(self)