potassium 6.0.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)