├── .editorconfig ├── .env ├── .erdconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── labels.json ├── .gitignore ├── .rubocop.yml ├── .rubocop_todo.yml ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── Rakefile ├── apiary.apib ├── app ├── admin │ ├── admin_user.rb │ ├── code_school.rb │ ├── dashboard.rb │ ├── location.rb │ ├── scholarship.rb │ ├── scholarship_application.rb │ ├── service.rb │ ├── team_member.rb │ └── user.rb ├── assets │ ├── javascripts │ │ └── active_admin.js.coffee │ └── stylesheets │ │ └── active_admin.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── api │ │ └── v1 │ │ │ ├── airtable │ │ │ └── mentorships_controller.rb │ │ │ ├── code_schools_controller.rb │ │ │ ├── email_list_recipients_controller.rb │ │ │ ├── events_controller.rb │ │ │ ├── git_hub_statistics_controller.rb │ │ │ ├── locations_controller.rb │ │ │ ├── mentors_controller.rb │ │ │ ├── requests_controller.rb │ │ │ ├── resources_controller.rb │ │ │ ├── scholarship_applications_controller.rb │ │ │ ├── scholarships_controller.rb │ │ │ ├── services_controller.rb │ │ │ ├── sessions_controller.rb │ │ │ ├── slack_users_controller.rb │ │ │ ├── social_users_controller.rb │ │ │ ├── status_controller.rb │ │ │ ├── tags_controller.rb │ │ │ ├── team_members_controller.rb │ │ │ ├── users │ │ │ └── passwords_controller.rb │ │ │ ├── users_controller.rb │ │ │ └── votes_controller.rb │ ├── api_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ └── users │ │ ├── confirmations_controller.rb │ │ ├── omniauth_callbacks_controller.rb │ │ ├── passwords_controller.rb │ │ ├── registrations_controller.rb │ │ ├── sessions_controller.rb │ │ └── unlocks_controller.rb ├── inputs │ └── inet_input.rb ├── jobs │ ├── add_guest_to_send_grid_job.rb │ ├── add_user_to_send_grid_job.rb │ ├── application_job.rb │ ├── github_collect_statistics_job.rb │ ├── meetup_member_sync_job.rb │ ├── send_email_to_leaders_job.rb │ ├── slack_jobs.rb │ └── slack_jobs │ │ └── inviter_job.rb ├── lib │ ├── airtable │ │ ├── client.rb │ │ ├── error.rb │ │ └── mentorship.rb │ ├── code_schools.rb │ ├── discourse │ │ └── single_sign_on.rb │ ├── format_data.rb │ ├── git_hub │ │ ├── authentication.rb │ │ ├── client.rb │ │ ├── committer.rb │ │ ├── issues.rb │ │ ├── page_compiler.rb │ │ ├── presenter.rb │ │ ├── pull_requests.rb │ │ ├── settings.rb │ │ └── structure.rb │ ├── id_me.rb │ ├── json_web_token.rb │ ├── meetup.rb │ ├── seed_team_members.rb │ ├── send_grid_client.rb │ ├── send_grid_client │ │ ├── guest.rb │ │ └── response.rb │ ├── slack_service │ │ ├── client.rb │ │ └── error.rb │ └── users_by_location.rb ├── mailers │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── ability.rb │ ├── admin_user.rb │ ├── application_record.rb │ ├── code_school.rb │ ├── concerns │ │ └── .keep │ ├── event.rb │ ├── git_hub_statistic.rb │ ├── git_hub_user.rb │ ├── location.rb │ ├── request.rb │ ├── resource.rb │ ├── role.rb │ ├── scholarship.rb │ ├── scholarship_application.rb │ ├── service.rb │ ├── slack_user.rb │ ├── team_member.rb │ ├── user.rb │ └── vote.rb ├── policies │ └── application_policy.rb ├── serializers │ ├── code_school_serializer.rb │ ├── location_serializer.rb │ ├── mentor_serializer.rb │ ├── request_serializer.rb │ └── user_serializer.rb └── views │ ├── devise │ ├── confirmations │ │ └── new.html.erb │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── email_changed.html.erb │ │ ├── password_change.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── sessions │ │ └── new.html.erb │ ├── shared │ │ └── _links.html.erb │ └── unlocks │ │ └── new.html.erb │ ├── layouts │ ├── mailer.html.erb │ └── mailer.text.erb │ └── user_mailer │ ├── new_user_in_leader_area.html.erb │ └── welcome.html.erb ├── bin ├── bundle ├── deploy ├── publish ├── rails ├── rake ├── rancher_update ├── setup └── update ├── config.ru ├── config ├── application.rb ├── board_members.yml ├── boot.rb ├── cable.yml ├── code_schools.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── active_admin.rb │ ├── application_controller_renderer.rb │ ├── backtrace_silencers.rb │ ├── core_extensions │ │ └── devise │ │ │ └── strategies │ │ │ └── json_web_token.rb │ ├── cors.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── sidekiq.rb │ └── wrap_parameters.rb ├── job_schedule.yml ├── locales │ ├── devise.en.yml │ └── en.yml ├── puma.rb ├── resources.yml ├── routes.rb ├── secrets.yml ├── session_store.rb ├── spring.rb └── team_members.yml ├── db ├── migrate │ ├── 20170401221959_create_users.rb │ ├── 20170420033746_add_devise_to_users.rb │ ├── 20170519144023_add_fields_to_users.rb │ ├── 20170519145444_create_requests.rb │ ├── 20170519184936_create_services.rb │ ├── 20170520144243_create_squads.rb │ ├── 20170520150128_create_squad_mentors.rb │ ├── 20170520180320_add_members_to_squads.rb │ ├── 20170520232041_add_status_to_requests.rb │ ├── 20170613012405_create_events.rb │ ├── 20170616161847_change_url_to_url.rb │ ├── 20170622013242_add_verified_to_user.rb │ ├── 20170629014745_create_team_members.rb │ ├── 20170701172700_create_resources.rb │ ├── 20170701172846_create_votes.rb │ ├── 20170701194816_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb │ ├── 20170701194817_add_missing_unique_indices.acts_as_taggable_on_engine.rb │ ├── 20170701194818_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb │ ├── 20170701194819_add_missing_taggable_index.acts_as_taggable_on_engine.rb │ ├── 20170701194820_change_collation_for_tag_names.acts_as_taggable_on_engine.rb │ ├── 20170701194821_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb │ ├── 20170720215043_add_state_to_user.rb │ ├── 20170723213338_add_more_attributes_to_user.rb │ ├── 20170802201510_create_scholarships.rb │ ├── 20170802202307_create_applications.rb │ ├── 20170803215307_rename_applications_to_scholarship_applications.rb │ ├── 20170803223854_rename_start_date_to_open_time.rb │ ├── 20170806214216_add_additional_fields_for_profiles_to_users.rb │ ├── 20170807033541_create_git_hub_users.rb │ ├── 20170807033815_create_git_hub_statistics.rb │ ├── 20170809164559_add_scholarship_info_field_to_users.rb │ ├── 20170813163239_add_columns_to_events.rb │ ├── 20170821193457_change_scholarship_application.rb │ ├── 20170903130609_add_index_to_source_events.rb │ ├── 20170920093119_create_code_schools.rb │ ├── 20170920093217_create_locations.rb │ ├── 20170921055331_add_notes_to_code_schools.rb │ ├── 20171013230050_devise_create_admin_users.rb │ ├── 20171013230053_create_active_admin_comments.rb │ ├── 20171022215418_drop_squads.rb │ ├── 20180129223616_add_group_to_team_members.rb │ ├── 20180204235116_add_mooc_to_code_schools.rb │ ├── 20180401034441_create_roles.rb │ ├── 20180408172532_add_role_id_to_admin_user.rb │ ├── 20180416024105_add_email_to_team_member.rb │ ├── 20180420192231_add_is_partner_to_code_schools.rb │ ├── 20180422030623_create_slack_user.rb │ ├── 20180422031009_drop_user_slack_id.rb │ ├── 20180430161218_add_rep_name_to_codeschool.rb │ └── 20180711212702_add_militarystatus_to_user.rb ├── schema.rb └── seeds.rb ├── docker-compose.yml ├── docs ├── data_models │ ├── request.png │ └── squad.png ├── models │ └── generating_model_graph.md ├── setup │ ├── aws_vm_setup.md │ ├── docker_setup.md │ └── native_setup.md └── testing │ └── testing.md ├── lib └── tasks │ ├── .keep │ ├── auto_generate_diagram.rake │ ├── meetup.rake │ ├── resource.rake │ ├── roles.rake │ ├── schools_from_yml.rake │ ├── seed_members.rake │ ├── test.rake │ ├── update_is_partner_for_code_schools.rake │ ├── update_sabio.rake │ ├── update_schools_logo.rake │ └── users.rake ├── log └── .keep ├── public └── robots.txt ├── test ├── cassettes │ ├── airtable │ │ ├── client │ │ │ ├── get_records_successful.yml │ │ │ └── post_record_successful.yml │ │ └── mentorship │ │ │ ├── exceeded_rate_limit.yml │ │ │ ├── post_multiple_successful.yml │ │ │ ├── post_successful.yml │ │ │ └── successful.yml │ ├── git_hub │ │ ├── commits │ │ │ ├── slashbot │ │ │ │ ├── pr_6.yml │ │ │ │ └── pr_7.yml │ │ │ └── successful.yml │ │ ├── pull_request │ │ │ ├── slashbot │ │ │ │ ├── pr_6.yml │ │ │ │ └── pr_7.yml │ │ │ └── successful.yml │ │ └── search │ │ │ ├── issues │ │ │ └── successful.yml │ │ │ └── pull_requests │ │ │ ├── compile_prs.yml │ │ │ ├── success_for_multi_page.yml │ │ │ └── success_for_single_page.yml │ ├── slack_service │ │ └── client │ │ │ ├── invite_failure.yml │ │ │ └── invite_success.yml │ └── user_test │ │ └── accepts_non_us_zipcodes_uk_.yml ├── controllers │ ├── .keep │ └── api │ │ └── v1 │ │ ├── airtable │ │ └── mentorships_controller_test.rb │ │ ├── code_schools_controller_test.rb │ │ ├── email_list_recipients_controller_test.rb │ │ ├── locations_controller_test.rb │ │ ├── mentors_controller_test.rb │ │ ├── requests_controller_test.rb │ │ ├── resources_controller_test.rb │ │ ├── services_controller_test.rb │ │ ├── sessions_controller_test.rb │ │ ├── slack_users_controller_test.rb │ │ ├── social_users_controller_test.rb │ │ ├── tags_controller_test.rb │ │ ├── team_members_controller_test.rb │ │ ├── users_controller_test.rb │ │ └── votes_controller_test.rb ├── factories │ ├── code_schools.rb │ ├── events.rb │ ├── git_hub_statistics.rb │ ├── git_hub_users.rb │ ├── locations.rb │ ├── requests.rb │ ├── resources.rb │ ├── roles.rb │ ├── scholarship_applications.rb │ ├── scholarships.rb │ ├── services.rb │ ├── team_members.rb │ ├── users.rb │ └── votes.rb ├── integration │ ├── .keep │ └── events_create_test.rb ├── jobs │ ├── add_guest_to_send_grid_job_test.rb │ ├── add_user_to_send_grid_job_test.rb │ └── meetup_member_sync_job_test.rb ├── lib │ ├── code_schools_test.rb │ ├── format_data_test.rb │ ├── git_hub │ │ ├── authentication_test.rb │ │ ├── committer_test.rb │ │ └── presenter_test.rb │ ├── meetup_test.rb │ ├── seed_team_members_test.rb │ ├── send_grid_client │ │ └── guest_test.rb │ ├── send_grid_client_test.rb │ ├── update_school_logos_test.rb │ └── users_by_location_test.rb ├── mailers │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── code_schools_test.rb │ ├── event_test.rb │ ├── git_hub_statistic_test.rb │ ├── request_test.rb │ ├── resource_test.rb │ ├── role_test.rb │ ├── scholarship_test.rb │ ├── team_member_test.rb │ └── user_test.rb ├── requests │ ├── airtable │ │ ├── client_test.rb │ │ └── mentorship_test.rb │ ├── git_hub │ │ ├── client_test.rb │ │ ├── issues_test.rb │ │ ├── page_compiler_test.rb │ │ └── pull_requests_test.rb │ └── slack_service │ │ └── client_test.rb ├── support │ ├── code_schools │ │ └── test_config.yml │ └── meetups │ │ └── sample_api_responses.rb └── test_helper.rb └── tmp └── .keep /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Tab indentation (no size specified) 13 | [Makefile] 14 | indent_style = tab 15 | 16 | [**.rb] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | # Matches the exact files either package.json or .travis.yml 21 | [.travis.yml] 22 | indent_style = space 23 | indent_size = 2 24 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=operationcodebackend 2 | REDIS_URL=redis://redis:6379/0 3 | RAILS_ENV=development 4 | POSTGRES_USER=postgres 5 | POSTGRES_HOST=operationcode-psql 6 | SLACK_SERVICE_URL=http://pybot.operationcode.org:8080/pybot/api/v1/slack 7 | SLACK_SERVICE_AUTH_TOKEN=XXXXX -------------------------------------------------------------------------------- /.erdconfig: -------------------------------------------------------------------------------- 1 | filename: docs/models/model_graph 2 | filetype: png 3 | title: "OperationCode Backend Domain Models" 4 | # List of model attributes to include in graph 5 | attribute: 6 | - foreign_keys 7 | - primary_keys 8 | - timestamps 9 | - inheritance 10 | - content 11 | # uses a notation style with more information built in. See documentation in docs/models/generating_model_graph.md 12 | notation: bachman 13 | orientation: horizontal 14 | # hides warnings from output when generating diagram 15 | warn: false 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | # Feature 3 | ## Why is this feature being added? 4 | 5 | 6 | ## What should your feature do? 7 | 8 | --- 9 | 10 | # Bug Report 11 | ## What is the current behavior? 12 | 13 | ## What is the expected behavior? 14 | 15 | ## What steps did you take to get this behavior? 16 | 17 | ## Additional Info 18 | ### Operating System 19 | 20 | ### Browser 21 | 22 | ### Screenshots 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description of changes 2 | 3 | 4 | # Issue Resolved 5 | 6 | Fixes # 7 | -------------------------------------------------------------------------------- /.github/labels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "name": "beginner friendly", "color": "#c2e0c6" }, 3 | { "name": "Status: Blocked", "color": "#e11d21" }, 4 | { "name": "Status: Duplicate", "color": "#fef2c0" }, 5 | { "name": "Status: In Progress", "color": "#cccccc" }, 6 | { "name": "Status: Available", "color": "#006b75" }, 7 | { "name": "Status: On Hold", "color": "#e11d21" }, 8 | { "name": "Type: Bug", "color": "#e11d21" }, 9 | { "name": "Type: Feature", "color": "#84b6eb" }, 10 | { "name": "Type: Question", "color": "#cc317c" }, 11 | { "name": "Priority: Low", "color": "#009800" }, 12 | { "name": "Priority: Medium", "color": "#fbca04" }, 13 | { "name": "Priority: High", "color": "#eb6420" }, 14 | { "name": "Priority: Critical", "color": "#e11d21" }, 15 | { "name": "Needs: Copy/Content", "color": "#d4c5f9" }, 16 | { "name": "Needs: More Detail", "color": "#b60205" } 17 | ] 18 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | AllCops: 4 | Exclude: 5 | - 'db/schema.rb' 6 | 7 | # Cop supports --auto-correct. 8 | # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. 9 | # SupportedStyles: single_quotes, double_quotes 10 | Style/StringLiterals: 11 | Exclude: 12 | - 'db/**/*' 13 | 14 | #turn off end of line checks, git will fix this 15 | EndOfLine: 16 | Enabled: false 17 | 18 | Metrics/ClassLength: 19 | Exclude: 20 | - 'test/models/user_test.rb' 21 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | -global -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.3 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Since we build and test in Docker we dont 2 | # need node at build time 3 | language: c 4 | 5 | services: 6 | - docker 7 | 8 | script: 9 | - make build 10 | - | 11 | while ! psql --host=localhost --username=postgres -c 'SELECT 1'> /dev/null 2>&1; do 12 | echo 'Waiting for postgres...' 13 | sleep 1; 14 | done; 15 | - make test 16 | - if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then make upgrade; fi 17 | 18 | env: 19 | secure: "AmnH0r1tF9HIVsUfhoJHzCEqTZz3TU4FUd2D+T0XzSDbz6kUSubmP4agYRsJI1xPTwegqSonwL0QGAlaIKEssz6rqAqBSvzHC95XvghP1HVVp2/j8vvFaJbpWsnTQ25FbMgDD89wHRKRs0qk2Ui0zi+CtNFylj83D13yoVys6C0=" 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6.3 2 | 3 | RUN printf "deb http://deb.debian.org/debian/ stretch main\ndeb-src http://deb.debian.org/debian/ stretch main\ndeb http://security.debian.org stretch/updates main\ndeb-src http://security.debian.org stretch/updates main" > /etc/apt/sources.list 4 | 5 | RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs graphviz 6 | 7 | ENV APP_HOME /app 8 | 9 | RUN mkdir $APP_HOME 10 | WORKDIR $APP_HOME 11 | 12 | COPY Gemfile $APP_HOME/ 13 | COPY Gemfile.lock $APP_HOME/ 14 | 15 | ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \ 16 | BUNDLE_JOBS=2 \ 17 | BUNDLE_PATH=/bundle 18 | 19 | RUN gem update --system && \ 20 | gem install bundler --no-document --version $(tail -n 1 Gemfile.lock) | sed -e 's/^[[:space:]]*//' && \ 21 | bundle install --system 22 | 23 | COPY . $APP_HOME/ 24 | 25 | CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | #ruby=ruby-2.3.3 2 | #ruby-gemset=operationcode_backend 3 | 4 | source 'https://rubygems.org' 5 | 6 | git_source(:github) do |repo_name| 7 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/') 8 | "https://github.com/#{repo_name}.git" 9 | end 10 | 11 | gem 'activeadmin', '~> 1.1' 12 | gem 'active_model_serializers' 13 | gem 'acts-as-taggable-on', '~> 5.0' 14 | gem 'cancancan', '~> 2.0' 15 | gem 'devise' 16 | gem 'geocoder' 17 | gem 'gibbon' 18 | gem 'httparty' 19 | gem 'jwt' 20 | gem 'lograge' 21 | gem 'logstash-event' 22 | gem 'operationcode-slack' 23 | gem 'pg', '~> 0.18' 24 | gem 'puma', '~> 3.0' 25 | gem 'rack-cors' 26 | gem 'rails', '~> 5.0.2' 27 | gem 'redis', '~> 3.0' 28 | gem 'rubocop', '~> 0.55.0', require: false 29 | gem 'sendgrid-ruby' 30 | gem 'sentry-raven' 31 | gem 'sidekiq' 32 | gem 'sidekiq-cron' 33 | gem 'skylight' 34 | gem 'pry-rails' 35 | 36 | group :development, :test do 37 | gem 'awesome_print', '~> 1.8' 38 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 39 | gem 'byebug', platform: :mri 40 | gem 'factory_girl_rails' 41 | gem 'faker' 42 | gem 'mocha' 43 | gem 'vcr', '~> 4.0.0' 44 | gem 'webmock', '~> 3.5.1' 45 | end 46 | 47 | group :development do 48 | gem 'listen', '~> 3.0.5' 49 | gem 'rails-erd' 50 | gem 'spring' 51 | gem 'spring-watcher-listen', '~> 2.0.0' 52 | end 53 | 54 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 55 | 56 | 57 | # Use ActiveModel has_secure_password 58 | # gem 'bcrypt', '~> 3.1.7' 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Operation Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | This file lists how the Operation Code Back End project is maintained. When making changes to the system, this file tells you who needs to review your contribution - you need a simple majority of maintainers for the relevant subsystems to provide a 👍 on your pull request. Additionally, you need to not receive a veto from a Sergeant or the Project Lead. 4 | 5 | Check out [how Operation Code Open Source projects are maintained](https://github.com/OperationCode/START_HERE/blob/61cebc02875ef448679e1130d3a68ef2f855d6c4/open_source_maintenance_policy.md) for details on the process, how to become a maintainer, sergeant, or the project lead. 6 | 7 | # Project Lead 8 | 9 | * [Nell Shamrell-Harrington](http://www.github.com/nellshamrell) 10 | 11 | # Sergeant 12 | 13 | * [Colleen Schnettler](http://www.github.com/leenyburger) 14 | * [Robb Kidd](http://www.github.com/robbkidd) 15 | 16 | # Maintainers 17 | 18 | * [William Montgomery](http://www.github.com/wimo7083) 19 | 20 | # Founding Contributors 21 | 22 | * [Harry Levine](https://github.com/hpjaj) -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/admin/admin_user.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register AdminUser do 2 | permit_params :email, :password, :password_confirmation, :role_id 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | column :email 8 | 9 | column 'Role' do |admin_user| 10 | admin_user.role.title 11 | end 12 | 13 | column :current_sign_in_at 14 | column :sign_in_count 15 | column :created_at 16 | actions 17 | end 18 | 19 | filter :email 20 | filter :current_sign_in_at 21 | filter :sign_in_count 22 | filter :created_at 23 | 24 | form do |f| 25 | f.inputs do 26 | f.input :email 27 | 28 | if current_admin_user.role.super_admin? 29 | f.input :role_id, label: 'Role', as: :select, collection: Role.all.order(:title).map { |role| [role.title, role.id]}, include_blank: false 30 | end 31 | 32 | f.input :password 33 | f.input :password_confirmation 34 | end 35 | 36 | f.actions 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/admin/code_school.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register CodeSchool do 2 | permit_params :name, :url, :logo, :full_time, :hardware_included, :has_online, :online_only, :created_at, :updated_at, :notes, :mooc, :rep_name, :rep_email, :is_partner 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :url 10 | column :logo 11 | column :full_time 12 | column :hardware_included 13 | column :has_online 14 | column :online_only 15 | column :created_at 16 | column :updated_at 17 | column :notes 18 | column :mooc 19 | column :rep_name 20 | column :rep_email 21 | column :is_partner 22 | column :created_at 23 | column :updated_at 24 | 25 | actions 26 | end 27 | 28 | form do |f| 29 | f.inputs do 30 | f.input :name 31 | f.input :url 32 | f.input :logo 33 | f.input :full_time 34 | f.input :hardware_included 35 | f.input :has_online 36 | f.input :online_only 37 | f.input :notes 38 | f.input :mooc 39 | f.input :rep_name 40 | f.input :rep_email 41 | f.input :is_partner 42 | end 43 | 44 | f.actions 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/admin/dashboard.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register_page 'Dashboard' do 2 | menu priority: 1, label: proc{ I18n.t('active_admin.dashboard') } 3 | 4 | content title: proc{ I18n.t('active_admin.dashboard') } do 5 | columns do 6 | column do 7 | panel 'New User Counts' do 8 | para "Today: #{User.count_created_since(Date.today)}" 9 | para "In the last week: #{User.count_created_since(1.week.ago)}" 10 | para "In the last month: #{User.count_created_since(1.month.ago)}" 11 | para "In the last year: #{User.count_created_since(1.year.ago)}" 12 | end 13 | end 14 | 15 | column do 16 | panel 'Verified Users' do 17 | para User.verified.count 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/admin/location.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Location do 2 | permit_params :va_accepted, :address1, :address2, :city, :state, :zip, :code_school_id 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column 'Code School' do |location| 9 | location.code_school.name 10 | end 11 | 12 | column :va_accepted 13 | column :address1 14 | column :address2 15 | column :city 16 | column :state 17 | column :zip 18 | column :created_at 19 | column :updated_at 20 | 21 | actions 22 | end 23 | 24 | form do |f| 25 | f.inputs do 26 | f.input :code_school_id, label: 'Code School', as: :select, collection: CodeSchool.all.order(:name).map { |school| [school.name, school.id]}, include_blank: false 27 | f.input :va_accepted 28 | f.input :address1 29 | f.input :address2 30 | f.input :city 31 | f.input :state 32 | f.input :zip 33 | end 34 | 35 | f.actions 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/admin/scholarship.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Scholarship do 2 | permit_params :name, :description, :location, :terms, :open_time, :close_time, :created_at, :updated_at 3 | index do 4 | selectable_column 5 | id_column 6 | 7 | column :name 8 | column :description 9 | column :location 10 | column :terms 11 | column :open_time 12 | column :close_time 13 | column :created_at 14 | column :updated_at 15 | 16 | actions 17 | end 18 | 19 | form do |f| 20 | f.inputs do 21 | f.input :name 22 | f.input :description 23 | f.input :location 24 | f.input :terms 25 | f.input :open_time 26 | f.input :close_time 27 | end 28 | 29 | f.actions 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/admin/scholarship_application.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register ScholarshipApplication do 2 | permit_params :reason, :terms_accepted, :scholarship_id, :user_id 3 | index do 4 | selectable_column 5 | id_column 6 | 7 | column 'Scholarship' do |scholarship_id| 8 | scholarship_id.scholarship.name 9 | end 10 | 11 | column 'User' do |user_id| 12 | user_id.user.name 13 | end 14 | 15 | column :reason 16 | column :terms_accepted 17 | column :scholarship_id 18 | column :user_id 19 | column :created_at 20 | column :updated_at 21 | 22 | actions 23 | end 24 | 25 | form do |f| 26 | f.inputs do 27 | f.input :scholarship_id, label: 'Scholarship', as: :select, collection: Scholarship.all.order(:name).map { |scholarship| [scholarship.name, scholarship.id] }, include_blank: false 28 | f.input :user_id, label: 'User', as: :select, collection: User.verified.order(:last_name).map { |user| [user.name, user.id] }, include_blank: false 29 | f.input :reason 30 | f.input :terms_accepted 31 | end 32 | 33 | f.actions 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/admin/service.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Service do 2 | permit_params :name 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :created_at 10 | column :updated_at 11 | 12 | actions 13 | end 14 | 15 | form do |f| 16 | f.inputs do 17 | f.input :name 18 | end 19 | 20 | f.actions 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/admin/team_member.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register TeamMember do 2 | permit_params :name, :role, :description, :group, :image_src, :email 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :role 10 | column :email 11 | column :created_at 12 | column :updated_at 13 | column :description 14 | column :group 15 | column :image_src 16 | 17 | actions 18 | end 19 | 20 | form do |f| 21 | f.inputs do 22 | f.input :name 23 | f.input :role 24 | f.input :email 25 | f.input :description 26 | f.input :group 27 | f.input :image_src 28 | end 29 | 30 | f.actions 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/assets/javascripts/active_admin.js.coffee: -------------------------------------------------------------------------------- 1 | #= require active_admin/base 2 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin.scss: -------------------------------------------------------------------------------- 1 | // SASS variable overrides must be declared before loading up Active Admin's styles. 2 | // 3 | // To view the variables that Active Admin provides, take a look at 4 | // `app/assets/stylesheets/active_admin/mixins/_variables.scss` in the 5 | // Active Admin source. 6 | // 7 | // For example, to change the sidebar width: 8 | // $sidebar-width: 242px; 9 | 10 | // Active Admin's got SASS! 11 | @import "active_admin/mixins"; 12 | @import "active_admin/base"; 13 | 14 | // Overriding any non-variable SASS must be done after the fact. 15 | // For example, to change the default status-tag color: 16 | // 17 | // .status_tag { background: #6090DB; } 18 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/api/v1/airtable/mentorships_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | module Airtable 4 | class MentorshipsController < ApiController 5 | before_action :authenticate_user! 6 | 7 | def index 8 | mentorship_data = ::Airtable::Mentorship.new.mentor_request_data 9 | 10 | render json: mentorship_data, status: :ok 11 | rescue ::Airtable::Error => e 12 | render json: { error: e.message }, status: :unprocessable_entity 13 | end 14 | 15 | def create 16 | mentor_request = ::Airtable::Mentorship.new.create_mentor_request(mentorship_params) 17 | 18 | render json: mentor_request, status: :created 19 | rescue ::Airtable::Error => e 20 | render json: { error: e.message }, status: :unprocessable_entity 21 | end 22 | 23 | private 24 | 25 | def mentorship_params 26 | params.permit( 27 | :slack_user, 28 | :email, 29 | :services, 30 | :skillsets, 31 | :additional_details, 32 | :mentor_requested 33 | ) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/controllers/api/v1/code_schools_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class CodeSchoolsController < ApiController 4 | before_action :authenticate_user!, only: [:create, :update, :destroy] 5 | 6 | def index 7 | render json: CodeSchool.order(:name).includes(:locations) 8 | end 9 | 10 | def create 11 | school = CodeSchool.new(code_school_params) 12 | 13 | if school.save 14 | render json: school 15 | else 16 | render json: { errors: school.errors.full_messages } 17 | end 18 | end 19 | 20 | def show 21 | school = CodeSchool.find_by(id: params[:id]) 22 | if school 23 | render json: school 24 | else 25 | render json: { error: 'No such record' }, status: :not_found 26 | end 27 | end 28 | 29 | def update 30 | school = CodeSchool.find(params[:id]) 31 | if school.update(code_school_params) 32 | render json: school 33 | else 34 | render json: { errors: school.errors.full_messages } 35 | end 36 | end 37 | 38 | def destroy 39 | school = CodeSchool.find(params[:id]) 40 | if school.destroy 41 | render json: { status: :ok } 42 | else 43 | render json: { errors: school.errors.full_messages } 44 | end 45 | end 46 | 47 | private 48 | 49 | def code_school_params 50 | params.require(:code_school).permit(:name, :url, :logo, :full_time, 51 | :hardware_included, :has_online, 52 | :online_only, :mooc, :is_partner, 53 | :rep_name, :rep_email) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/controllers/api/v1/email_list_recipients_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class EmailListRecipientsController < ApiController 4 | def create 5 | raise "Invalid email address: #{permitted_params[:email]}" unless valid_email? 6 | 7 | AddGuestToSendGridJob.perform_async(permitted_params[:email]) 8 | 9 | render json: { email: permitted_params[:email], guest: true }, status: :created 10 | rescue StandardError => e 11 | render json: { errors: e.message }, status: :unprocessable_entity 12 | end 13 | 14 | private 15 | 16 | def permitted_params 17 | params.permit(:email) 18 | end 19 | 20 | def valid_email? 21 | params[:email] =~ User::VALID_EMAIL 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/api/v1/events_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class EventsController < ApiController 4 | def index 5 | render json: Event.all, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/api/v1/git_hub_statistics_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class GitHubStatisticsController < ApiController 4 | def oc_totals 5 | render json: GitHub::Presenter.new(params).oc_totals, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | 10 | def totals_by_repository 11 | render json: GitHub::Presenter.new(params).totals_by_repository, status: :ok 12 | rescue StandardError => e 13 | render json: { errors: e.message }, status: :unprocessable_entity 14 | end 15 | 16 | def totals_for_user 17 | render json: GitHub::Presenter.new(params).totals_for_user, status: :ok 18 | rescue StandardError => e 19 | render json: { errors: e.message }, status: :unprocessable_entity 20 | end 21 | 22 | def totals_for_user_in_repository 23 | render json: GitHub::Presenter.new(params).totals_for_user_in_repository, status: :ok 24 | rescue StandardError => e 25 | render json: { errors: e.message }, status: :unprocessable_entity 26 | end 27 | 28 | def oc_averages 29 | render json: GitHub::Presenter.new(params).oc_averages, status: :ok 30 | rescue StandardError => e 31 | render json: { errors: e.message }, status: :unprocessable_entity 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/api/v1/locations_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class LocationsController < ApiController 4 | before_action :set_location, except: :create 5 | before_action :authenticate_user! 6 | 7 | def create 8 | location = Location.create!(location_params) 9 | 10 | render json: { location: location.id }, status: :created 11 | rescue ActiveRecord::RecordInvalid => e 12 | render json: { errors: e.message }, status: :unprocessable_entity 13 | end 14 | 15 | def update 16 | @location.update!(location_params) 17 | 18 | render json: { status: :ok } 19 | rescue ActiveRecord::RecordInvalid => e 20 | render json: { errors: e.message }, status: :unprocessable_entity 21 | end 22 | 23 | def destroy 24 | @location.destroy! 25 | 26 | render json: { status: :ok } 27 | rescue ActiveRecord::RecordNotDestroyed, NoMethodError => e 28 | render json: { errors: e.message }, status: :unprocessable_entity 29 | end 30 | 31 | private 32 | 33 | def set_location 34 | @location = Location.find_by(id: params[:id]) 35 | end 36 | 37 | def location_params 38 | params.require(:location).permit(:code_school_id, :va_accepted, :address1, :address2, :city, :state, :zip) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/api/v1/mentors_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class MentorsController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | mentors = User.mentors 8 | render json: mentors 9 | end 10 | 11 | def show 12 | mentor = User.mentors.find(params[:id]) 13 | render json: MentorSerializer.new(mentor) 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/v1/requests_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class RequestsController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | authorize! :read, Request 8 | render json: Request.unclaimed.order('created_at DESC') 9 | end 10 | 11 | def create 12 | @request = current_user.requests.create(request_params) 13 | render json: @request 14 | end 15 | 16 | def show 17 | @request = Request.find_by(id: params[:id]) 18 | authorize! :read, @request 19 | render json: @request 20 | end 21 | 22 | def update 23 | request = Request.find_by(id: params[:id]) 24 | if request.update(request_params) 25 | render json: request 26 | else 27 | render nothing: true, status: :bad_request 28 | end 29 | end 30 | 31 | private 32 | 33 | def request_params 34 | params.require(:request) 35 | .permit(:details, :language, 36 | :service_id, :requested_mentor_id, 37 | :status, :assigned_mentor_id) 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/api/v1/scholarship_applications_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ScholarshipApplicationsController < ApiController 4 | def create 5 | current_user.scholarship_applications.create! scholarship_application_params 6 | 7 | render json: { redirect_to: '/success' }, status: :created 8 | rescue StandardError => e 9 | render json: { error: e.message }, status: :bad_request 10 | end 11 | 12 | private 13 | 14 | def scholarship_application_params 15 | params.require(:scholarship_application).permit(:reason, :terms_accepted, :scholarship_id) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/api/v1/scholarships_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ScholarshipsController < ApiController 4 | def index 5 | render json: Scholarship.unexpired 6 | end 7 | 8 | def show 9 | scholarship = Scholarship.find_by(id: params[:id]) 10 | if scholarship 11 | render json: scholarship 12 | else 13 | render json: { error: 'No such record' }, status: :not_found 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/v1/services_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ServicesController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | services = Service.all 8 | render json: services 9 | end 10 | 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/api/v1/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SessionsController < Devise::SessionsController 4 | respond_to :json 5 | before_action :set_default_response_format 6 | before_action :authenticate_user!, only: %i[sso] 7 | 8 | def create 9 | self.resource = warden.authenticate!(auth_options) 10 | sign_in(resource_name, resource) 11 | 12 | set_sso_response if sso_request? 13 | @redirect_path ||= '/profile' 14 | 15 | render json: { 16 | token: resource.token, 17 | user: UserSerializer.new(current_user), 18 | redirect_to: @redirect_path 19 | } 20 | end 21 | 22 | def sso 23 | set_sso_response 24 | render json: { redirect_to: @redirect_path } 25 | end 26 | 27 | def set_default_response_format 28 | request.format = 'json' 29 | end 30 | 31 | private 32 | 33 | def sso_request? 34 | params[:sso].present? && params[:sig].present? 35 | end 36 | 37 | def sso_params 38 | { sso: params[:sso], sig: params[:sig] } 39 | end 40 | 41 | def set_sso_response 42 | @sso = Discourse::SingleSignOn.parse(sso_params, Discourse::SingleSignOn::SECRET) 43 | @sso.email = current_user.email 44 | @sso.name = current_user.name 45 | @sso.username = current_user.email 46 | @sso.external_id = current_user.id 47 | @sso.custom_fields['verified'] = current_user.verified 48 | @sso.add_groups = 'mentors' if current_user.mentor 49 | @sso.sso_secret = Discourse::SingleSignOn::SECRET 50 | 51 | @redirect_path = @sso.to_url('https://community.operationcode.org/session/sso_login') 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /app/controllers/api/v1/slack_users_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SlackUsersController < ApiController 4 | before_action :authenticate_py_bot! 5 | 6 | def create 7 | # TODO: Placeholder for PyBot call to create new record 8 | end 9 | 10 | def access 11 | render json: { message: 'You have access!' }, status: :ok 12 | end 13 | 14 | private 15 | 16 | def authenticate_py_bot! 17 | auth = JsonWebToken.decode(auth_token) 18 | 19 | if auth['key'] == Rails.application.secrets.py_bot_auth_key 20 | true 21 | elsif auth.dig(:errors).present? 22 | render json: { errors: auth.dig(:errors) }, status: :unauthorized 23 | else 24 | render json: { errors: ['Auth token is invalid'] }, status: :unauthorized 25 | end 26 | end 27 | 28 | def auth_token 29 | @auth_token ||= request.headers.fetch('Authorization', '').split(' ').last 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/api/v1/social_users_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SocialUsersController < ApiController 4 | 5 | # For social media logins, renders the appropriate `redirect_to` path depending on whether the user is registered. 6 | # 7 | # Requires that the ActionController::Parameters contain an :email key. 8 | # For example: { email: "john@example.com" } 9 | # 10 | # @return [String] A string of the user's redirect_to path 11 | # @see https://github.com/zquestz/omniauth-google-oauth2#devise 12 | # 13 | def show 14 | user = User.find_by(email: params.dig(:email)); 15 | redirect_path = '/login' 16 | 17 | if user.nil? 18 | redirect_path = '/social_login' 19 | end 20 | render json: { redirect_to: redirect_path } 21 | end 22 | 23 | # For social media logins, creates the user in the database if necessary, 24 | # then logs them in, and renders the appropriate `redirect_to` path depending on whether the user is logging in 25 | # for the first time. 26 | # 27 | # @return [String] A string of the user's redirect_to path 28 | # @return [Json] A serialied JSON object derived from current_user 29 | # @return [Token] A token that the frontend stores to know the user is logged in 30 | # @see https://github.com/OperationCode/operationcode_backend/blob/master/app/controllers/api/v1/sessions_controller.rb#L8-L20 31 | # 32 | def create 33 | @user, redirect_path = User.fetch_social_user_and_redirect_path(params.dig(:user)) 34 | 35 | if @user.save 36 | sign_in @user, event: :authenticate_user 37 | render json: { 38 | token: @user.token, 39 | user: UserSerializer.new(current_user), 40 | redirect_to: redirect_path 41 | } 42 | else 43 | redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n") 44 | end 45 | end 46 | 47 | private 48 | 49 | def social_user_params 50 | params.require(:user).permit( 51 | :email, 52 | :first_name, 53 | :last_name, 54 | :zip, 55 | :password 56 | ) 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /app/controllers/api/v1/status_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class StatusController < ApiController 4 | before_action :authenticate_user!, only: :protected 5 | 6 | def all 7 | render json: { status: :ok } 8 | end 9 | 10 | def protected 11 | render json: { status: :ok, protected: true } 12 | end 13 | 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/api/v1/tags_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class TagsController < ApiController 4 | def index 5 | render json: ActsAsTaggableOn::Tag.all, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/api/v1/team_members_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class TeamMembersController < ApiController 4 | before_action :authenticate_user!, except: :index 5 | 6 | def index 7 | render json: TeamMember.all, status: :ok 8 | end 9 | 10 | def create 11 | team_member = TeamMember.create! team_member_params 12 | 13 | render json: { team_member: team_member.id }, status: :created 14 | rescue StandardError => e 15 | render json: { errors: e.message }, status: :unprocessable_entity 16 | end 17 | 18 | def update 19 | team_member = TeamMember.find params[:id] 20 | 21 | team_member.update! team_member_params 22 | render json: { status: :ok } 23 | rescue StandardError => e 24 | render json: { errors: e.message }, status: :unprocessable_entity 25 | end 26 | 27 | def destroy 28 | team_member = TeamMember.find params[:id] 29 | 30 | team_member.destroy! 31 | render json: { status: :ok } 32 | rescue StandardError => e 33 | render json: { errors: e.message }, status: :unprocessable_entity 34 | end 35 | 36 | private 37 | 38 | def team_member_params 39 | params.permit(:name, :role, :group, :description, :image_src, :email) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/api/v1/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | module Users 4 | class PasswordsController < ApiController 5 | 6 | # Resets password token and sends reset password instructions by email. 7 | # 8 | # @see http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Models/Recoverable 9 | # 10 | def forgot 11 | user = User.find_by email: params[:email] 12 | raise 'Could not find a user with that email address' unless user.present? 13 | user.generate_password_token! 14 | user.send_reset_password_instructions 15 | render json: { status: :ok } 16 | rescue StandardError => e 17 | render json: { errors: e.message }, status: :unprocessable_entity 18 | end 19 | 20 | def reset 21 | token = params[:reset_password_token].to_s 22 | user = User.with_reset_password_token(token) 23 | 24 | if user.present? && user.password_token_valid? 25 | if user.reset_password!(params[:password]) 26 | render json: { status: 'ok' }, status: :ok 27 | else 28 | render json: { error: user.errors.full_messages }, status: :unprocessable_entity 29 | end 30 | else 31 | render json: { error: ['Link not valid or expired. Try generating a new link.'] }, status: :not_found 32 | end 33 | end 34 | 35 | def update 36 | render json: { error: 'Password not present' }, status: :unprocessable_entity unless params[:password].present 37 | 38 | if current_user.reset_password(params[:password]) 39 | render json: { status: 'ok' }, status: :ok 40 | else 41 | render json: { errors: current_user.errors.full_messages }, status: :unprocessable_entity 42 | end 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/controllers/api/v1/votes_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class VotesController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def create 7 | vote = current_user.votes.create! resource_id: params[:resource_id] 8 | 9 | render json: { vote: vote.id }, status: :ok 10 | rescue StandardError => e 11 | render json: { errors: e.message }, status: :unprocessable_entity 12 | end 13 | 14 | def destroy 15 | vote = Vote.find params[:id] 16 | 17 | vote.destroy! 18 | render json: { status: :ok } 19 | rescue StandardError => e 20 | render json: { errors: e.message }, status: :unprocessable_entity 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/api_controller.rb: -------------------------------------------------------------------------------- 1 | class ApiController < ActionController::API 2 | include CanCan::ControllerAdditions 3 | wrap_parameters format: [:json] 4 | 5 | rescue_from CanCan::AccessDenied do |exception| 6 | exception.default_message = 'You are not authorized to access this resource.' 7 | render json: { error: exception.message }, status: :forbidden 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :null_session 5 | 6 | def admin_access_denied(exception) 7 | redirect_to admin_root_path, alert: exception.message 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/users/confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::ConfirmationsController < Devise::ConfirmationsController 2 | # GET /resource/confirmation/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/confirmation 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/confirmation?confirmation_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after resending confirmation instructions. 20 | # def after_resending_confirmation_instructions_path_for(resource_name) 21 | # super(resource_name) 22 | # end 23 | 24 | # The path used after confirmation. 25 | # def after_confirmation_path_for(resource_name, resource) 26 | # super(resource_name, resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | # You should configure your model like this: 3 | # devise :omniauthable, omniauth_providers: [:twitter] 4 | 5 | # You should also create an action method in this controller like this: 6 | # def twitter 7 | # end 8 | 9 | # More info at: 10 | # https://github.com/plataformatec/devise#omniauth 11 | 12 | # GET|POST /resource/auth/twitter 13 | # def passthru 14 | # super 15 | # end 16 | 17 | # GET|POST /users/auth/twitter/callback 18 | # def failure 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # The path used when OmniAuth fails 25 | # def after_omniauth_failure_path_for(scope) 26 | # super(scope) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::PasswordsController < Devise::PasswordsController 2 | # GET /resource/password/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/password 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/password/edit?reset_password_token=abcdef 13 | # def edit 14 | # super 15 | # end 16 | 17 | # PUT /resource/password 18 | # def update 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # def after_resetting_password_path_for(resource) 25 | # super(resource) 26 | # end 27 | 28 | # The path used after sending reset password instructions 29 | # def after_sending_reset_password_instructions_path_for(resource_name) 30 | # super(resource_name) 31 | # end 32 | end 33 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::RegistrationsController < Devise::RegistrationsController 2 | # before_action :configure_sign_up_params, only: [:create] 3 | # before_action :configure_account_update_params, only: [:update] 4 | 5 | # GET /resource/sign_up 6 | # def new 7 | # super 8 | # end 9 | 10 | # POST /resource 11 | # def create 12 | # super 13 | # end 14 | 15 | # GET /resource/edit 16 | # def edit 17 | # super 18 | # end 19 | 20 | # PUT /resource 21 | # def update 22 | # super 23 | # end 24 | 25 | # DELETE /resource 26 | # def destroy 27 | # super 28 | # end 29 | 30 | # GET /resource/cancel 31 | # Forces the session data which is usually expired after sign 32 | # in to be expired now. This is useful if the user wants to 33 | # cancel oauth signing in/up in the middle of the process, 34 | # removing all OAuth session data. 35 | # def cancel 36 | # super 37 | # end 38 | 39 | # protected 40 | 41 | # If you have extra params to permit, append them to the sanitizer. 42 | # def configure_sign_up_params 43 | # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) 44 | # end 45 | 46 | # If you have extra params to permit, append them to the sanitizer. 47 | # def configure_account_update_params 48 | # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) 49 | # end 50 | 51 | # The path used after sign up. 52 | # def after_sign_up_path_for(resource) 53 | # super(resource) 54 | # end 55 | 56 | # The path used after sign up for inactive accounts. 57 | # def after_inactive_sign_up_path_for(resource) 58 | # super(resource) 59 | # end 60 | end 61 | -------------------------------------------------------------------------------- /app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SessionsController < Devise::SessionsController 2 | # before_action :configure_sign_in_params, only: [:create] 3 | 4 | # GET /resource/sign_in 5 | # def new 6 | # super 7 | # end 8 | 9 | # POST /resource/sign_in 10 | # def create 11 | # super 12 | # end 13 | 14 | # DELETE /resource/sign_out 15 | # def destroy 16 | # super 17 | # end 18 | 19 | # protected 20 | 21 | # If you have extra params to permit, append them to the sanitizer. 22 | # def configure_sign_in_params 23 | # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute]) 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/users/unlocks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::UnlocksController < Devise::UnlocksController 2 | # GET /resource/unlock/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/unlock 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/unlock?unlock_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after sending unlock password instructions 20 | # def after_sending_unlock_instructions_path_for(resource) 21 | # super(resource) 22 | # end 23 | 24 | # The path used after unlocking the resource 25 | # def after_unlock_path_for(resource) 26 | # super(resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/inputs/inet_input.rb: -------------------------------------------------------------------------------- 1 | class InetInput < Formtastic::Inputs::StringInput 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/add_guest_to_send_grid_job.rb: -------------------------------------------------------------------------------- 1 | class AddGuestToSendGridJob 2 | include Sidekiq::Worker 3 | 4 | # Adds the passed guest to our Contact List in SendGrid. 5 | # 6 | # @param guest_email [String] A string of the guest's email 7 | # 8 | def perform(guest_email) 9 | SendGridClient.new.add_user guest(guest_email) 10 | end 11 | 12 | private 13 | 14 | # Creates a user-like Struct, representing a guest. 15 | # 16 | # @param guest_email [String] A string of the guest's email 17 | # @return [Struct] Returns a user-like Struct 18 | # 19 | def guest(guest_email) 20 | SendGridClient::Guest.user(guest_email) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/jobs/add_user_to_send_grid_job.rb: -------------------------------------------------------------------------------- 1 | class AddUserToSendGridJob < ApplicationJob 2 | include Sidekiq::Worker 3 | 4 | def perform(user_id) 5 | user = User.find(user_id) 6 | SendGridClient.new.add_user(user) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob 2 | include Sidekiq::Worker 3 | end 4 | -------------------------------------------------------------------------------- /app/jobs/github_collect_statistics_job.rb: -------------------------------------------------------------------------------- 1 | class GithubCollectStatisticsJob 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | limit = GitHub::Client.new.rate_limit 6 | logger.info("Github Ratelimit: #{limit}") 7 | 8 | GitHub::PullRequests.new.fetch_and_save! 9 | GitHub::Issues.new.fetch_and_save! 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/jobs/meetup_member_sync_job.rb: -------------------------------------------------------------------------------- 1 | class MeetupMemberSyncJob 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | meetup_client = Meetup.new 6 | members = meetup_client.members_by_email 7 | 8 | User.where(email: members.keys).each do |user| 9 | member = members[user.email] 10 | user.latitude ||= member['lat'] 11 | user.longitude ||= member['lon'] 12 | user.city ||= member['city'] 13 | user.state ||= member['state'] 14 | 15 | # Only update a user if values have changed 16 | user.save if user.changed? 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/jobs/send_email_to_leaders_job.rb: -------------------------------------------------------------------------------- 1 | class SendEmailToLeadersJob < ApplicationJob 2 | include Sidekiq::Worker 3 | 4 | def perform(_user_id) 5 | logger.debug 'Deprecated pathway, trying to determine what is placing this on queue.' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/slack_jobs.rb: -------------------------------------------------------------------------------- 1 | class SlackJobs < ApplicationJob 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/slack_jobs/inviter_job.rb: -------------------------------------------------------------------------------- 1 | class SlackJobs 2 | class InviterJob < SlackJobs 3 | include Sidekiq::Worker 4 | sidekiq_options retry: 5 5 | 6 | def perform(email) 7 | Raven.user_context email: email 8 | Raven.tags_context slack_invite: 'yes' 9 | 10 | SlackService::Client.new.invite(email) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/lib/airtable/error.rb: -------------------------------------------------------------------------------- 1 | module Airtable 2 | class Error < StandardError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/lib/airtable/mentorship.rb: -------------------------------------------------------------------------------- 1 | module Airtable 2 | class Mentorship 3 | attr_reader :client 4 | 5 | def initialize 6 | @client = Airtable::Client.new 7 | end 8 | 9 | # Fetches all the records from the mentors, services, and skillsets Airtables, 10 | # and re-formats the data into a custom hash 11 | # 12 | # @return [Hash] Hash of mentor, services, and skillsets data 13 | # 14 | def mentor_request_data 15 | { 16 | mentors: data_for('Mentors', name_key: 'Full Name'), 17 | services: data_for('Services'), 18 | skillsets: data_for('Skillsets') 19 | } 20 | end 21 | 22 | # Creates a new mentor request record in the Mentor Request Airtable 23 | # 24 | # @param body [Hash] Hash of mentor request attributes 25 | # @return [Hash] Hash of the newly created mentor request Airtable record 26 | # 27 | def create_mentor_request(body) 28 | request_body = { 29 | 'fields' => { 30 | 'Slack User' => body[:slack_user], 31 | 'Email' => body[:email], 32 | 'Service' => format_for_posting(body[:services]), 33 | 'Skillsets' => format_for_posting(body[:skillsets]), 34 | 'Additional Details' => body[:additional_details], 35 | 'Mentor Requested' => format_for_posting(body[:mentor_requested]) 36 | } 37 | }.to_json 38 | validate_user(body[:email]) 39 | client.post_record('Mentor Request', request_body) 40 | end 41 | 42 | private 43 | 44 | def data_for(table, name_key: 'Name') 45 | records = client.get_records_for(table) 46 | 47 | records['records'].map do |record| 48 | { 49 | id: record['id'], 50 | name: record.dig('fields', name_key) 51 | } 52 | end 53 | end 54 | 55 | def validate_user(email) 56 | return if Rails.env.test? # I really don't like this, TODO: replace with a proper mock 57 | return unless Slack::Utils.new.email_is_registered(email) 58 | raise Airtable::Error, "#{email} is not registered on slack" 59 | end 60 | 61 | # Converts a comma separated string into an array of strings 62 | # 63 | # @param data_string [String] Comma separated strings of data 64 | # @return [Array] An array of stripped strings 65 | # 66 | def format_for_posting(data_string) 67 | data_string.split(',').map(&:strip) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/lib/code_schools.rb: -------------------------------------------------------------------------------- 1 | class CodeSchools 2 | def self.all(config = Rails.root + 'config/code_schools.yml') 3 | YAML.load_file(config) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/lib/format_data.rb: -------------------------------------------------------------------------------- 1 | class FormatData 2 | 3 | # Converts a comma-separated string into an array of its contents. 4 | # 5 | # @param comma_separated_string [String] String of comma-separated values, i.e '80123, 80202' 6 | # @return [Array] An array of strings, i.e. ['80123', '80202'] 7 | # 8 | def self.csv_to_array(comma_separated_string) 9 | comma_separated_string.upcase.split(',').map(&:strip) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/lib/git_hub/authentication.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Authentication 3 | BASIC = 'basic_authentication' 4 | O_AUTH_2_TOKEN = 'o_auth_2_token' 5 | O_AUTH_2_KEY_SECRET = 'o_auth_2_key_secret' 6 | 7 | attr_reader :options, :auth_level 8 | 9 | def initialize(options) 10 | @options = options 11 | @auth_level = GitHub::Settings.authentication_level 12 | end 13 | 14 | def set_options 15 | return options unless Rails.env.prod? 16 | 17 | authentication_options.deep_merge options 18 | end 19 | 20 | private 21 | 22 | def authentication_options 23 | case auth_level 24 | when BASIC 25 | # tbd 26 | when O_AUTH_2_TOKEN 27 | o_auth_2_token_options 28 | when O_AUTH_2_KEY_SECRET 29 | key_secret_options 30 | end 31 | end 32 | 33 | def o_auth_2_token_options 34 | { 35 | headers: { 36 | 'Authorization' => "Bearer #{GitHub::Settings.o_auth_2_token}" 37 | } 38 | } 39 | end 40 | 41 | def key_secret_options 42 | { 43 | query: { 44 | client_id: GitHub::Settings.client_id, 45 | client_secret: GitHub::Settings.client_secret, 46 | } 47 | } 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /app/lib/git_hub/committer.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Committer 3 | def self.find_or_create_user!(user_attrs) 4 | GitHubUser.find_or_create_by!(git_hub_id: user_attrs[:id]) do |user| 5 | user.git_hub_login = user_attrs[:login] 6 | user.avatar_url = user_attrs[:avatar_url] 7 | user.api_url = user_attrs[:api_url] 8 | user.html_url = user_attrs[:html_url] 9 | end 10 | end 11 | 12 | def self.find_or_create_statistic!(stat_attrs, source_type, git_hub_user_id) 13 | GitHubStatistic.find_or_create_by!(source_type: source_type, source_id: stat_attrs[:source_id]) do |stat| 14 | stat.git_hub_user_id = git_hub_user_id 15 | stat.state = stat_attrs[:state] 16 | stat.additions = stat_attrs[:additions].to_i 17 | stat.deletions = stat_attrs[:deletions].to_i 18 | stat.repository = stat_attrs[:repository] 19 | stat.url = stat_attrs[:url] 20 | stat.title = stat_attrs[:title] 21 | stat.number = stat_attrs[:number] 22 | stat.completed_on = stat_attrs[:closed_on] 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/lib/git_hub/issues.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Issues 3 | attr_reader :client, :compiled_issues, :existing_issue_ids, :date 4 | 5 | def initialize 6 | @client = GitHub::Client.new 7 | @compiled_issues = [] 8 | @existing_issue_ids = GitHubStatistic.issues.pluck(:source_id) 9 | @date = GitHubStatistic.last_issue_completed_on 10 | end 11 | 12 | def fetch_and_save!(print_results: true) 13 | get_issues.each do |issue| 14 | git_hub_user = GitHub::Committer.find_or_create_user! issue[:git_hub_user] 15 | git_hub_issue_stat = GitHub::Committer.find_or_create_statistic! issue, issue[:source_type], git_hub_user.id 16 | 17 | next unless print_results 18 | p 'git_hub_user:' 19 | p git_hub_user 20 | 21 | p 'git_hub_issue_stat:' 22 | p git_hub_issue_stat 23 | end 24 | end 25 | 26 | private 27 | 28 | def get_issues 29 | client.repositories.each do |repo| 30 | build_details_for repo 31 | end 32 | 33 | compiled_issues 34 | end 35 | 36 | def build_details_for(repo) 37 | get_all_issues_for(repo).each do |issue| 38 | next if existing_issue_ids.include? issue['id'].to_s 39 | next unless assignee_present? issue 40 | 41 | compiled_issues << GitHub::Structure.new(issue, repo).issue 42 | end 43 | end 44 | 45 | def get_all_issues_for(repo) 46 | query = { 47 | repo: repo, 48 | type: 'issue', 49 | is: 'closed', 50 | page_number: 1, 51 | filters: "+closed:>=#{date}" 52 | } 53 | response = client.search_for(query) 54 | 55 | if response.headers['link'].present? 56 | GitHub::PageCompiler.new(query, response, client).compile_prs 57 | else 58 | response['items'] 59 | end 60 | end 61 | 62 | def assignee_present?(issue) 63 | issue['assignee'].present? && issue['assignee']['login'].present? 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /app/lib/git_hub/page_compiler.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class PageCompiler 3 | attr_reader :search_params, :response, :page_count, :client 4 | 5 | # @see https://developer.github.com/v3/guides/traversing-with-pagination/#consuming-the-information 6 | # 7 | def initialize(search_params, response, client) 8 | @search_params = search_params 9 | @response = response 10 | @page = 2 11 | @page_count = pages_in(response) - 1 12 | @client = client 13 | end 14 | 15 | def compile_prs 16 | compiled_prs = response['items'] 17 | 18 | page_count.times do 19 | pr_response = client.search_for(search_params.merge({ page_number: @page })) 20 | 21 | compiled_prs << pr_response['items'] 22 | 23 | @page += 1 24 | end 25 | 26 | compiled_prs.flatten 27 | end 28 | 29 | def compile_commits 30 | compiled_commits = response.parsed_response 31 | 32 | page_count.times do 33 | response << client.commits_for(search_params[:repo], search_params[:pr_number], @page) 34 | 35 | compiled_commits << response.parsed_response 36 | 37 | @page += 1 38 | end 39 | 40 | compiled_commits.flatten 41 | end 42 | 43 | private 44 | 45 | def pages_in(response) 46 | pages = response.headers['link'].split(',').last.match(/&page=(\d+).*$/)[1] 47 | 48 | pages.to_i 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/lib/git_hub/settings.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Settings 3 | BACKEND = 'operationcode_backend' 4 | FRONTEND = 'operationcode_frontend' 5 | OC = 'operationcode' 6 | BOT = 'operationcode_bot' 7 | SLASHBOT = 'operationcode_slashbot' 8 | 9 | def self.owner 10 | 'OperationCode' 11 | end 12 | 13 | def self.repositories 14 | [BACKEND, FRONTEND, OC, BOT, SLASHBOT] 15 | end 16 | 17 | # @see https://developer.github.com/v3/#user-agent-required 18 | # 19 | def self.user_agent 20 | owner 21 | end 22 | 23 | # Number of items that are returned per page, per request. 24 | # Max of 100. 25 | # 26 | # @see https://developer.github.com/v3/#pagination 27 | # 28 | def self.per_page 29 | 100 30 | end 31 | 32 | def self.client_id 33 | Rails.application.secrets.git_hub_client_id 34 | end 35 | 36 | def self.client_secret 37 | Rails.application.secrets.git_hub_client_secret 38 | end 39 | 40 | def self.o_auth_2_token 41 | Rails.application.secrets.git_hub_oauth_token 42 | end 43 | 44 | # There are three ways to authenticate through GitHub's API v3. 45 | # Choices are: BASIC, O_AUTH_2_TOKEN, or O_AUTH_2_KEY_SECRET. 46 | # 47 | # This method sets the authentication level from constants in 48 | # GitHub::Authentication 49 | # 50 | # @see https://developer.github.com/v3/#authentication 51 | # 52 | def self.authentication_level 53 | GitHub::Authentication::O_AUTH_2_KEY_SECRET 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /app/lib/git_hub/structure.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Structure 3 | attr_reader :repository, :response 4 | 5 | def initialize(response, repository) 6 | @response = response 7 | @repository = repository 8 | end 9 | 10 | def pull_request 11 | { 12 | source_type: GitHubStatistic::PR, 13 | source_id: response['id'], 14 | repository: repository, 15 | number: response['number'], 16 | state: response['state'], 17 | additions: response['additions'], 18 | deletions: response['deletions'], 19 | url: response['html_url'], 20 | title: response['title'], 21 | closed_on: response['merged_at'].to_date, 22 | git_hub_user: append_git_hub_user('user') 23 | } 24 | end 25 | 26 | def commit 27 | { 28 | source_type: GitHubStatistic::COMMIT, 29 | source_id: response['sha'], 30 | url: response['html_url'], 31 | title: response['commit']['message'], 32 | repository: repository, 33 | closed_on: response['commit']['committer']['date'].to_date, 34 | git_hub_user: append_git_hub_user('author') 35 | } 36 | end 37 | 38 | def issue 39 | { 40 | source_type: GitHubStatistic::ISSUE, 41 | source_id: response['id'], 42 | url: response['html_url'], 43 | title: response['title'], 44 | number: response['number'], 45 | state: response['state'], 46 | repository: repository, 47 | closed_on: response['closed_at'].to_date, 48 | git_hub_user: append_git_hub_user('assignee') 49 | } 50 | end 51 | 52 | private 53 | 54 | def append_git_hub_user(user_key) 55 | { 56 | login: response[user_key]['login'], 57 | avatar_url: response[user_key]['avatar_url'], 58 | api_url: response[user_key]['url'], 59 | html_url: response[user_key]['html_url'], 60 | id: response[user_key]['id'], 61 | } 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /app/lib/id_me.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | 3 | class IdMe 4 | include HTTParty 5 | base_uri 'api.id.me' 6 | 7 | private_class_method :headers 8 | 9 | def self.verify!(access_token) 10 | options = { headers: headers } 11 | 12 | response = get("/api/public/v2/attributes.json?access_token=#{access_token}", options) 13 | 14 | verified = response.body['verified'] == 'verified' ? true : false 15 | 16 | fail response.body if !verified 17 | 18 | verified 19 | end 20 | 21 | def self.headers 22 | { 'Accepts' => 'application/json' } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/lib/json_web_token.rb: -------------------------------------------------------------------------------- 1 | class JsonWebToken 2 | def self.encode(payload, expiration = nil) 3 | expiration ||= Rails.application.secrets.jwt_expiration_hours 4 | 5 | payload = payload.dup 6 | payload['exp'] = expiration.to_i.hours.from_now.to_i 7 | 8 | JWT.encode(payload, Rails.application.secrets.jwt_secret) 9 | rescue => e 10 | raise "Failed to encode JsonWebToken due to: #{e}" 11 | end 12 | 13 | def self.decode(token) 14 | decoded_token = JWT.decode(token, Rails.application.secrets.jwt_secret) 15 | 16 | decoded_token.first 17 | rescue JWT::ExpiredSignature 18 | { errors: ['Auth token has expired'], status: 401 } 19 | rescue JWT::DecodeError 20 | { errors: ['Invalid auth token'], status: 401 } 21 | rescue => e 22 | Rails.logger.debug "Failed to decode token: '#{token}'" 23 | Rails.logger.debug e 24 | nil 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/lib/seed_team_members.rb: -------------------------------------------------------------------------------- 1 | class SeedTeamMembers 2 | BOARD_DATA_PATH = 'board_members.yml'.freeze 3 | TEAM_DATA_PATH = 'team_members.yml'.freeze 4 | class << self 5 | def from_yaml(file_name, group) 6 | members_seed_file = Rails.root.join('config', file_name) 7 | members = YAML.load_file(members_seed_file) 8 | members.each do |member| 9 | create_or_update(member.merge(group: group)) 10 | end 11 | end 12 | 13 | def seed_all 14 | seed_board 15 | seed_team 16 | end 17 | 18 | def seed_board 19 | from_yaml( 20 | BOARD_DATA_PATH, 21 | TeamMember::BOARD_GROUP_NAME 22 | ) 23 | end 24 | 25 | def seed_team 26 | from_yaml( 27 | TEAM_DATA_PATH, 28 | TeamMember::TEAM_GROUP_NAME 29 | ) 30 | end 31 | 32 | def clean_seed 33 | TeamMember.delete_all 34 | seed_all 35 | end 36 | 37 | private 38 | 39 | def create_or_update(member) 40 | TeamMember.find_or_create_by!(name: member['name']) do |c| 41 | c.update!(member) 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/lib/send_grid_client/guest.rb: -------------------------------------------------------------------------------- 1 | # The OC has end users that would like to be on our emailing list; however, 2 | # have not committed to creating a User account with us, yet. 3 | # 4 | # This class allows us to create Structs for these 'guests', that respond 5 | # identically to a User object, within the SendGridClient class. 6 | # 7 | class SendGridClient::Guest 8 | Visitor = Struct.new(:id, :email, :first_name, :last_name) do 9 | def attributes 10 | to_h.as_json 11 | end 12 | end 13 | 14 | # Creates a user-like Struct, to represent an OC guest. 15 | # 16 | # Defaults the :id attribute to 'guest-id', as their is no database id; 17 | # however, the id needs to be logged for debugging in the SendGridClient. 18 | # 19 | # @param email_address [String] A string of the guest's email address 20 | # @return [Struct] A user-like struct 21 | # 22 | def self.user(email_address) 23 | Visitor.new('guest-id', email_address, '', '') 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/lib/send_grid_client/response.rb: -------------------------------------------------------------------------------- 1 | class SendGridClient::Response 2 | attr_reader :body, :status_code 3 | 4 | def initialize(response) 5 | @response = response 6 | @body = JSON.parse(response.body) unless response.body.blank? 7 | @status_code = response.status_code 8 | end 9 | 10 | def successful? 11 | successful_status_code && successful_error_count 12 | end 13 | 14 | private 15 | 16 | def successful_status_code 17 | status_code == '201' 18 | end 19 | 20 | # Some requests don't return a body so we default to just checking 21 | # the status code 22 | # 23 | def successful_error_count 24 | return true if body.blank? 25 | 26 | body['error_count'] == 0 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/lib/slack_service/client.rb: -------------------------------------------------------------------------------- 1 | module SlackService 2 | class Client 3 | include HTTParty 4 | 5 | base_uri ENV['SLACK_SERVICE_URL'] 6 | 7 | attr_reader :headers 8 | 9 | def initialize 10 | bearer_token = "Bearer #{ENV['SLACK_SERVICE_AUTH_TOKEN']}" 11 | @headers = { Authorization: bearer_token } 12 | end 13 | 14 | def verify(email) 15 | response = self.class.get( 16 | '/verify', 17 | headers: headers, 18 | query: { email: email } 19 | ) 20 | 21 | Rails.logger.info "Slack service #verify response: #{response.inspect}" 22 | 23 | return_value_for(response) 24 | end 25 | 26 | def invite(email) 27 | response = self.class.post( 28 | '/invite', 29 | headers: headers, 30 | body: { email: email }.to_json 31 | ) 32 | 33 | Rails.logger.info "Slack service #invite response: #{response.inspect}" 34 | 35 | return_value_for(response) 36 | end 37 | 38 | private 39 | 40 | def response_json(response) 41 | response.present? ? response.parsed_response : {} 42 | end 43 | 44 | def return_value_for(response) 45 | json = response_json(response) 46 | case response.code 47 | when 200 48 | json 49 | else 50 | raise SlackService::Error, response.inspect 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/lib/slack_service/error.rb: -------------------------------------------------------------------------------- 1 | module SlackService 2 | class Error < StandardError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/lib/users_by_location.rb: -------------------------------------------------------------------------------- 1 | class UsersByLocation 2 | def initialize(params) 3 | @state = params[:state] 4 | @zip = params[:zip] 5 | @city = params[:city] 6 | @lat_long = params[:lat_long] 7 | @radius = params[:radius] || 20 8 | end 9 | 10 | def count 11 | if @state.present? 12 | User.count_by_state @state 13 | elsif @zip.present? 14 | User.count_by_zip @zip 15 | elsif @city.present? 16 | User.count_by_location @city, @radius 17 | elsif @lat_long.present? 18 | User.count_by_location @lat_long, @radius 19 | else 20 | raise 'A city, zip code, or latitude/longitude must be provided' 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | default from: 'staff@operationcode.org' 3 | 4 | def welcome(user) 5 | @user = user 6 | mail to: @user.email, subject: 'Welcome to Operation Code!' 7 | end 8 | 9 | def new_user_in_leader_area(leader, new_user) 10 | @new_user = new_user 11 | mail to: leader.email, subject: 'A new user has joined within 50 miles of you' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | 2 | class Ability 3 | include CanCan::Ability 4 | 5 | # Follows CanCanCan best practices for granting permissions. 6 | # 7 | # @see https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practices#give-permissions-dont-take-them-away 8 | # 9 | def initialize(user) 10 | return unless user.persisted? 11 | 12 | if user.class == AdminUser 13 | return unless user.role&.board_accessible? 14 | can :read, ActiveAdmin::Page, name: 'Dashboard', namespace_name: 'admin' 15 | can :read, CodeSchool 16 | can :read, Location 17 | can :read, Scholarship 18 | can :read, ScholarshipApplication 19 | can :read, Service 20 | can :read, TeamMember 21 | can :read, User 22 | can [:read, :update], AdminUser, id: user.id 23 | 24 | return unless user.role&.admin_accessible? 25 | can :read, :all 26 | can :manage, CodeSchool 27 | can :manage, Location 28 | can :manage, Scholarship 29 | can :manage, ScholarshipApplication 30 | can :manage, Service 31 | can :manage, TeamMember 32 | 33 | return unless user.role&.super_admin? 34 | can :manage, :all 35 | else 36 | return unless user.mentor? 37 | can :read, Request 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/models/admin_user.rb: -------------------------------------------------------------------------------- 1 | class AdminUser < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable and :omniauthable 4 | devise :database_authenticatable, 5 | :recoverable, :rememberable, :trackable, :validatable 6 | 7 | belongs_to :role 8 | 9 | before_save :set_role 10 | 11 | private 12 | 13 | def set_role 14 | if role_id.blank? 15 | self.role_id = Role.find_by(title: Role::BOARD)&.id 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/code_school.rb: -------------------------------------------------------------------------------- 1 | class CodeSchool < ApplicationRecord 2 | validates :name, :url, :logo, presence: true 3 | validates_inclusion_of :full_time, :hardware_included, :has_online, :online_only, :in => [true, false] 4 | has_many :locations, -> { order('state ASC, city ASC') }, dependent: :destroy 5 | end 6 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/event.rb: -------------------------------------------------------------------------------- 1 | class Event < ApplicationRecord 2 | validates :name, :start_date, :source_updated, :source_id, :source_type, :address1, :city, presence: true 3 | validates :source_id, uniqueness: { scope: :source_type } 4 | 5 | VALID_URL_REGEX = URI::regexp 6 | validates :url, format: {with:VALID_URL_REGEX} 7 | end 8 | -------------------------------------------------------------------------------- /app/models/git_hub_user.rb: -------------------------------------------------------------------------------- 1 | class GitHubUser < ApplicationRecord 2 | has_many :git_hub_statistics 3 | 4 | validates :git_hub_login, presence: true 5 | end 6 | -------------------------------------------------------------------------------- /app/models/location.rb: -------------------------------------------------------------------------------- 1 | class Location < ApplicationRecord 2 | belongs_to :code_school 3 | validates :address1, :city, :state, :zip, presence: true 4 | validates :va_accepted, inclusion: { in: [true, false] } 5 | end 6 | -------------------------------------------------------------------------------- /app/models/request.rb: -------------------------------------------------------------------------------- 1 | class Request < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :requested_mentor, class_name: 'User', optional: true 4 | belongs_to :assigned_mentor, class_name: 'User', optional: true 5 | belongs_to :service 6 | 7 | validates_presence_of :user 8 | 9 | scope :unclaimed, -> { where(assigned_mentor: nil) } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/resource.rb: -------------------------------------------------------------------------------- 1 | class Resource < ApplicationRecord 2 | acts_as_taggable 3 | 4 | has_many :votes 5 | 6 | validates :name, :url, :category, presence: true 7 | 8 | # Uses ActsAsTaggableOn gem to return relevant Resources. 9 | # 10 | # @see https://github.com/mbleigh/acts-as-taggable-on#usage 11 | # 12 | def self.with_tags(tags='') 13 | if tags.present? 14 | tagged_with(tags, any: true, parse: true) 15 | else 16 | all 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | SUPER_ADMIN = 'super_admin' 3 | ADMIN = 'admin' 4 | BOARD = 'board_member' 5 | 6 | has_many :users 7 | has_many :admin_users 8 | 9 | validates :title, presence: true, uniqueness: true 10 | 11 | def board_accessible? 12 | [BOARD, ADMIN, SUPER_ADMIN].include? title 13 | end 14 | 15 | def admin_accessible? 16 | [ADMIN, SUPER_ADMIN].include? title 17 | end 18 | 19 | def super_admin? 20 | title == SUPER_ADMIN 21 | end 22 | 23 | def admin? 24 | title == ADMIN 25 | end 26 | 27 | def board? 28 | title == BOARD 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/models/scholarship.rb: -------------------------------------------------------------------------------- 1 | class Scholarship < ApplicationRecord 2 | has_many :scholarship_applications 3 | 4 | scope :unexpired, -> { where('close_time > ? OR close_time IS NULL', Time.current) } 5 | end 6 | -------------------------------------------------------------------------------- /app/models/scholarship_application.rb: -------------------------------------------------------------------------------- 1 | class ScholarshipApplication < ApplicationRecord 2 | validates :user_id, uniqueness: {scope: :scholarship_id} 3 | 4 | belongs_to :user 5 | belongs_to :scholarship 6 | 7 | delegate :name, to: :user, prefix: true 8 | delegate :email, to: :user, prefix: true 9 | end 10 | -------------------------------------------------------------------------------- /app/models/service.rb: -------------------------------------------------------------------------------- 1 | class Service < ApplicationRecord 2 | validates_presence_of :name 3 | end 4 | -------------------------------------------------------------------------------- /app/models/slack_user.rb: -------------------------------------------------------------------------------- 1 | class SlackUser < ApplicationRecord 2 | validates :slack_id, :slack_name, :slack_real_name, :slack_email, :user_id, presence: true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/team_member.rb: -------------------------------------------------------------------------------- 1 | class TeamMember < ApplicationRecord 2 | BOARD_GROUP_NAME = 'board'.freeze 3 | EXECUTIVE_GROUP_NAME = 'executive'.freeze 4 | TEAM_GROUP_NAME = 'team'.freeze 5 | 6 | validates :name, :role, :group, presence: true 7 | validates :group, inclusion: { 8 | in: [BOARD_GROUP_NAME, EXECUTIVE_GROUP_NAME, TEAM_GROUP_NAME] 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/vote.rb: -------------------------------------------------------------------------------- 1 | class Vote < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :resource, counter_cache: true 4 | 5 | validates :user_id, :resource_id, presence: true 6 | validates :user_id, uniqueness: { scope: :resource_id, message: 'has already voted for this resource' } 7 | end 8 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/serializers/code_school_serializer.rb: -------------------------------------------------------------------------------- 1 | class CodeSchoolSerializer < ActiveModel::Serializer 2 | attributes :name, :url, :logo, :full_time, :hardware_included, :has_online, :online_only, :mooc, :is_partner, :rep_name, :rep_email 3 | has_many :locations 4 | end 5 | -------------------------------------------------------------------------------- /app/serializers/location_serializer.rb: -------------------------------------------------------------------------------- 1 | class LocationSerializer < ActiveModel::Serializer 2 | attributes :va_accepted, :address1, :address2, :city, :state, :zip 3 | end 4 | -------------------------------------------------------------------------------- /app/serializers/mentor_serializer.rb: -------------------------------------------------------------------------------- 1 | class MentorSerializer < UserSerializer 2 | attributes :bio 3 | 4 | end 5 | -------------------------------------------------------------------------------- /app/serializers/request_serializer.rb: -------------------------------------------------------------------------------- 1 | class RequestSerializer < ActiveModel::Serializer 2 | attributes :id, 3 | :details, 4 | :language, 5 | :status, 6 | :created_at 7 | 8 | belongs_to :user 9 | belongs_to :requested_mentor 10 | belongs_to :assigned_mentor 11 | belongs_to :service 12 | end 13 | -------------------------------------------------------------------------------- /app/serializers/user_serializer.rb: -------------------------------------------------------------------------------- 1 | class UserSerializer < ActiveModel::Serializer 2 | attributes :id, :email, :zip, :first_name, :last_name, 3 | :mentor, :verified 4 | attributes :role 5 | 6 | def role 7 | object.role&.title 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> 9 |
10 | 11 |
12 | <%= f.submit "Resend confirmation instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/email_changed.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @email %>!

2 | 3 | <% if @resource.try(:unconfirmed_email?) %> 4 |

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

5 | <% else %> 6 |

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', "https://operationcode.org/reset_password?reset_password_token=#{@token}" %>

6 |

If you didn't request this, please ignore this email.

7 |

Your password won't change until you access the link above and create a new one.

8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
8 | <%= f.label :password, "New password" %>
9 | <% if @minimum_password_length %> 10 | (<%= @minimum_password_length %> characters minimum)
11 | <% end %> 12 | <%= f.password_field :password, autofocus: true, autocomplete: "off" %> 13 |
14 | 15 |
16 | <%= f.label :password_confirmation, "Confirm new password" %>
17 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 18 |
19 | 20 |
21 | <%= f.submit "Change my password" %> 22 |
23 | <% end %> 24 | 25 | <%= render "devise/shared/links" %> 26 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Send me reset password instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 12 |
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
13 | <% end %> 14 | 15 |
16 | <%= f.label :password %> (leave blank if you don't want to change it)
17 | <%= f.password_field :password, autocomplete: "off" %> 18 | <% if @minimum_password_length %> 19 |
20 | <%= @minimum_password_length %> characters minimum 21 | <% end %> 22 |
23 | 24 |
25 | <%= f.label :password_confirmation %>
26 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 27 |
28 | 29 |
30 | <%= f.label :current_password %> (we need your current password to confirm your changes)
31 | <%= f.password_field :current_password, autocomplete: "off" %> 32 |
33 | 34 |
35 | <%= f.submit "Update" %> 36 |
37 | <% end %> 38 | 39 |

Cancel my account

40 | 41 |

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

42 | 43 | <%= link_to "Back", :back %> 44 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.label :password %> 13 | <% if @minimum_password_length %> 14 | (<%= @minimum_password_length %> characters minimum) 15 | <% end %>
16 | <%= f.password_field :password, autocomplete: "off" %> 17 |
18 | 19 |
20 | <%= f.label :password_confirmation %>
21 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 22 |
23 | 24 |
25 | <%= f.submit "Sign up" %> 26 |
27 | <% end %> 28 | 29 | <%= render "devise/shared/links" %> 30 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Log in

2 | 3 | <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 4 |
5 | <%= f.label :email %>
6 | <%= f.email_field :email, autofocus: true %> 7 |
8 | 9 |
10 | <%= f.label :password %>
11 | <%= f.password_field :password, autocomplete: "off" %> 12 |
13 | 14 | <% if devise_mapping.rememberable? -%> 15 |
16 | <%= f.check_box :remember_me %> 17 | <%= f.label :remember_me %> 18 |
19 | <% end -%> 20 | 21 |
22 | <%= f.submit "Log in" %> 23 |
24 | <% end %> 25 | 26 | <%= render "devise/shared/links" %> 27 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Resend unlock instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/user_mailer/new_user_in_leader_area.html.erb: -------------------------------------------------------------------------------- 1 | <%= @new_user.name %>, has joined. They're located within 50 miles of your chapter! 2 | Please email them at <%= @new_user.email %> and welcome them, and consider providing info on the next local meetup. 3 | Let's encourage in-person involvement within Operation Code! 4 | -------------------------------------------------------------------------------- /app/views/user_mailer/welcome.html.erb: -------------------------------------------------------------------------------- 1 |

2 | Welcome to Operation Code!
3 |

4 | 5 |

6 | Be on the lookout for an email invite to the Operation Code Slack community. Slack is where we communicate online in real-time to ask questions, collaborate, and share information. It's a pivotal aspect of our community! If you've never used Slack before, consider watching this instructional video on using the application.
7 |

8 | 9 |

10 | You've just joined a community of 3000+ software developers, veterans, and military spouses, who are all looking to further their skills and careers in tech! We are a decentralized organization, and we work both online (via Slack) and in local chapters across the country. We highly recommend giving yourself an introduction in the #general channel on Slack, and to sign up to go to your next local meetup! An example could be:
11 | "Hi all. My name is David. I'm from Washington and I'm interested in web development!" 12 | Please read our Code of Conduct, as well. 13 |

14 | 15 |

16 | Are you interested in helping Operation Code's website through code or design, and building your experience and portfolio? We're open source and love onboarding new developers! Read more here, and join the #oc-projects channel on Slack - feel free to ask about anything. 17 |

18 | 19 |

Again, welcome aboard, and don't forget to follow and share Operation Code on Twitter and Facebook.

20 | 21 |

22 | Cheers, and keep coding,
23 |

the Operation Code team :)
24 |

25 | 26 |

P.S. - If you do not receive an invite from Slack within 24 hours, please email our support team, or ping us on Twitter at @operation_code.

27 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | KUBE_CONFIG=kubectl.config 6 | 7 | # This script updates our kubernetes deploymnet from travis 8 | 9 | # Install kubectl 10 | curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 11 | chmod +x ./kubectl 12 | 13 | # Generate our kubectl config 14 | cat << EOF > $KUBE_CONFIG 15 | apiVersion: v1 16 | clusters: 17 | - cluster: 18 | certificate-authority-data: $KUBE_CA_DATA 19 | server: $KUBE_SERVER 20 | name: cluster-operationcode 21 | contexts: 22 | - context: 23 | cluster: cluster-operationcode 24 | namespace: operationcode 25 | user: $KUBE_USER 26 | name: operationcode 27 | - context: 28 | cluster: cluster-operationcode 29 | namespace: operationcode-staging 30 | user: $KUBE_USER 31 | name: staging 32 | current-context: operationcode 33 | kind: Config 34 | preferences: {} 35 | users: 36 | - name: $KUBE_USER 37 | user: 38 | client-certificate-data: $KUBE_CLIENT_CERT 39 | client-key-data: $KUBE_CLIENT_KEY 40 | EOF 41 | 42 | # Updates the deployment 43 | # 44 | # Container names come from: 45 | # https://github.com/OperationCode/operationcode_infra/blob/master/kubernetes/operationcode_backend/deployment.yml 46 | # 47 | # current-context 48 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend app=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} 49 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend sidekiq=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} 50 | # specifically do staging 51 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend app=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} -n operationcode-staging 52 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend sidekiq=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} -n operationcode-staging 53 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Publishes the most recent web container to docker hubs repo. 4 | # This script assumes docker push works. 5 | # You must set up docker push on your own. 6 | 7 | set -eu 8 | 9 | DOCKER_REPO="operationcode/operationcode_backend" 10 | 11 | RAILS_ROOT="$(dirname $0)/.." 12 | cd $RAILS_ROOT 13 | 14 | PROJECT_NAME=$(grep COMPOSE_PROJECT_NAME $RAILS_ROOT/.env | cut -d= -f2) 15 | 16 | IMAGE_NAME="${PROJECT_NAME}_web" 17 | IMAGE_ID=$(docker images $IMAGE_NAME:latest --format "{{.ID}}") 18 | 19 | if [ -n "$DOCKER_USERNAME" ]; then echo "Found username"; fi 20 | if [ -n "$DOCKER_PASSWORD" ]; then echo "Found password"; fi 21 | 22 | if [ -n "$DOCKER_USERNAME" ] && [ -n "$DOCKER_PASSWORD" ]; then 23 | echo "Logging in using ENV creds" 24 | docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 25 | fi 26 | 27 | docker tag $IMAGE_ID $DOCKER_REPO 28 | docker tag $IMAGE_ID ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 29 | docker push $DOCKER_REPO 30 | docker push ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 31 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails' 4 | # Pick the frameworks you want: 5 | require 'active_model/railtie' 6 | require 'active_job/railtie' 7 | require 'active_record/railtie' 8 | require 'action_controller/railtie' 9 | require 'action_mailer/railtie' 10 | require 'action_view/railtie' 11 | require 'action_cable/engine' 12 | require 'sprockets/railtie' 13 | require 'rails/test_unit/railtie' 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | module OperationcodeBackend 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | 25 | # Only loads a smaller set of middleware suitable for API only apps. 26 | # Middleware like session, flash, cookies can be added back manually. 27 | # Skip views, helpers and assets when generating a new resource. 28 | config.api_only = true 29 | 30 | config.middleware.use Rack::MethodOverride 31 | config.middleware.use ActionDispatch::Flash 32 | config.middleware.use ActionDispatch::Cookies 33 | config.middleware.use ActionDispatch::Session::CookieStore 34 | 35 | config.active_job.queue_adapter = :sidekiq 36 | config.log_level = :debug 37 | 38 | config.logger = Logger.new(STDOUT) 39 | config.lograge.enabled = true 40 | 41 | config.secret_path = Rails.root.join('run/secrets') 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | staging: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | 11 | production: 12 | adapter: redis 13 | url: redis://localhost:6379/1 14 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | user: <%= ENV['POSTGRES_USER'] %> 24 | host: <%= ENV['POSTGRES_HOST'] %> 25 | password: <%= ENV['POSTGRES_PASSWORD'] %> 26 | 27 | development: 28 | <<: *default 29 | database: operationcode_development 30 | 31 | test: 32 | <<: *default 33 | database: operationcode_test 34 | 35 | staging: 36 | <<: *default 37 | database: operationcode_production 38 | 39 | production: 40 | <<: *default 41 | host: <%= ENV['POSTGRES_HOST'].present? ? ENV['POSTGRES_HOST'] : 'invalid_postgres_host' %> 42 | database: operationcode_production 43 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | config.action_mailer.default_url_options = { host: 'localhost:3000' } 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | 44 | # Use an evented file watcher to asynchronously detect changes in source code, 45 | # routes, locales, etc. This feature depends on the listen gem. 46 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 47 | config.lograge.enabled = true 48 | config.lograge.formatter = Lograge::Formatters::Logstash.new 49 | end 50 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | 43 | config.log_level = :warn 44 | end 45 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/core_extensions/devise/strategies/json_web_token.rb: -------------------------------------------------------------------------------- 1 | module Devise 2 | module Strategies 3 | class JsonWebToken < Base 4 | def valid? 5 | request.headers['Authorization'].present? 6 | end 7 | 8 | def authenticate! 9 | return fail! unless claims 10 | return fail! unless claims.has_key?('user_id') 11 | 12 | success! User.find_by_id claims['user_id'] 13 | end 14 | 15 | protected 16 | 17 | def claims 18 | strategy, token = request.headers['Authorization'].split(' ') 19 | 20 | return nil unless valid_strategy?(strategy) 21 | 22 | claim = ::JsonWebToken.decode(token) 23 | Rails.logger.debug 'Claim decoded' 24 | claim 25 | rescue => e 26 | Rails.logger.debug "Failed to decode token #{token}" 27 | Rails.logger.debug e 28 | nil 29 | end 30 | 31 | def valid_strategy?(strategy) 32 | (strategy || '').downcase == 'bearer' 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | allow do 10 | origins '*' 11 | 12 | resource '*', 13 | headers: :any, 14 | methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 6 | 7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 8 | # Previous versions had false. 9 | ActiveSupport.to_time_preserves_timezone = true 10 | 11 | # Require `belongs_to` associations by default. Previous versions had false. 12 | Rails.application.config.active_record.belongs_to_required_by_default = true 13 | 14 | # Do not halt callback chains when a callback returns false. Previous versions had true. 15 | ActiveSupport.halt_callback_chains_on_return_false = false 16 | 17 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 18 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 19 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq' 2 | require 'sidekiq-cron' 3 | 4 | schedule_file = 'config/job_schedule.yml' 5 | 6 | Sidekiq.configure_server do |config| 7 | # https://stackoverflow.com/questions/28412913/disable-automatic-retry-with-activejob-used-with-sidekiq 8 | # prevent excessive retries from some default status. 9 | config.death_handlers << lambda { |job, e| 10 | # thanks rob for the nice message 11 | evt = Raven::Event.new(message: "#{job['class']} #{job['jid']} done run outta tries.") 12 | Raven.send_event(evt) 13 | } 14 | Rails.logger = Sidekiq::Logging.logger 15 | ActiveRecord::Base.logger = Sidekiq::Logging.logger 16 | 17 | Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) if File.exist?(schedule_file) 18 | end 19 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/job_schedule.yml: -------------------------------------------------------------------------------- 1 | ## Examples of job configurations 2 | 3 | #my_first_job: 4 | # cron: "*/5 * * * *" 5 | # class: "HardWorker" 6 | # queue: hard_worker 7 | # 8 | #second_job: 9 | # cron: "*/30 * * * *" # execute at every 30 minutes 10 | # class: "HardWorker" 11 | # queue: hard_worker_long 12 | # args: 13 | # hard: "stuff" 14 | 15 | 16 | # At 04:00 every day run the meetup member sync job 17 | meetup_member_sync_job: 18 | #cron: "0 4 * * *" 19 | cron: "0 0 5 31 2 ?" # only run Feb 31 20 | class: "MeetupMemberSyncJob" 21 | queue: "default" 22 | 23 | # At 03:00 every day run the github collect statistics job 24 | github_collect_statistics_job: 25 | #cron: "0 3 * * *" 26 | cron: "0 0 5 31 2 ?" # only run Feb 31 27 | class: "GithubCollectStatisticsJob" 28 | queue: "default" 29 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | activerecord: 25 | errors: 26 | models: 27 | scholarship_application: 28 | attributes: 29 | user_id: 30 | taken: "One application per scholarship." 31 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 2 | threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5) 3 | threads threads_count, threads_count 4 | 5 | preload_app! 6 | 7 | rackup DefaultRackup 8 | port ENV['PORT'] || 3000 9 | environment ENV['RACK_ENV'] || 'development' 10 | 11 | on_worker_boot do 12 | # Worker specific setup for Rails 4.1+ 13 | # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot 14 | ActiveRecord::Base.establish_connection 15 | end 16 | -------------------------------------------------------------------------------- /config/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_app_session' 4 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/team_members.yml: -------------------------------------------------------------------------------- 1 | # Operation Code Team Member Listing 2 | # Each entry should contain the following: 3 | #- name: 4 | # role: 5 | # 6 | # Each entry must start on a new line with - name: 7 | 8 | - name: Amy Tan 9 | role: CFO 10 | email: amy@operationcode.org 11 | 12 | - name: Ashley Templet 13 | role: CCO 14 | email: ashley@operationcode.org 15 | 16 | - name: David Reis 17 | role: COO acting 18 | 19 | - name: Jameel Matin 20 | role: Public Policy Director 21 | email: jameel@operationcode.org 22 | 23 | - name: Jennifer Weidman 24 | role: CCMO CDO 25 | email: jennifer@operationcode.org 26 | 27 | - name: Kelly MacLeod 28 | role: People Operations 29 | email: kelly@operationcode.org 30 | 31 | - name: Matthew Walter 32 | role: Infrastructure Lead 33 | 34 | - name: Harry Levine 35 | role: Backend Lead 36 | 37 | - name: Kyle Holmberg 38 | role: Frontend Lead 39 | 40 | - name: Maggie Molina 41 | role: Code Schools 42 | 43 | - name: Nell Shamrell 44 | role: CTO 45 | email: nell@operationcode.org 46 | 47 | - name: Stephano D'Aniello 48 | role: General Counsel 49 | -------------------------------------------------------------------------------- /db/migrate/20170401221959_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :email 5 | t.string :zip 6 | t.float :latitude 7 | t.float :longitude 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20170420033746_add_devise_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseToUsers < ActiveRecord::Migration[5.0] 2 | def self.up 3 | change_table :users do |t| 4 | ## Database authenticatable 5 | t.string :encrypted_password, null: false, default: "" 6 | 7 | ## Recoverable 8 | t.string :reset_password_token 9 | t.datetime :reset_password_sent_at 10 | 11 | ## Rememberable 12 | t.datetime :remember_created_at 13 | 14 | ## Trackable 15 | t.integer :sign_in_count, default: 0, null: false 16 | t.datetime :current_sign_in_at 17 | t.datetime :last_sign_in_at 18 | t.inet :current_sign_in_ip 19 | t.inet :last_sign_in_ip 20 | 21 | ## Confirmable 22 | # t.string :confirmation_token 23 | # t.datetime :confirmed_at 24 | # t.datetime :confirmation_sent_at 25 | # t.string :unconfirmed_email # Only if using reconfirmable 26 | 27 | ## Lockable 28 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 29 | # t.string :unlock_token # Only if unlock strategy is :email or :both 30 | # t.datetime :locked_at 31 | 32 | 33 | # Uncomment below if timestamps were not included in your original model. 34 | # t.timestamps null: false 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | 43 | def self.down 44 | # By default, we don't want to make any assumption about how to roll back a migration when your 45 | # model already existed. Please edit below which fields you would like to remove in this migration. 46 | raise ActiveRecord::IrreversibleMigration 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /db/migrate/20170519144023_add_fields_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddFieldsToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | change_table :users do |t| 4 | t.boolean :mentor, default: false 5 | t.string :slack_name 6 | t.string :first_name 7 | t.string :last_name 8 | t.string :timezone 9 | t.text :bio 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20170519145444_create_requests.rb: -------------------------------------------------------------------------------- 1 | class CreateRequests < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :requests do |t| 4 | t.belongs_to :service 5 | t.string :language 6 | t.text :details 7 | t.belongs_to :user, foreign_key: true 8 | t.belongs_to :assigned_mentor, references: :users 9 | t.belongs_to :requested_mentor, references: :users 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20170519184936_create_services.rb: -------------------------------------------------------------------------------- 1 | class CreateServices < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :services do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20170520144243_create_squads.rb: -------------------------------------------------------------------------------- 1 | class CreateSquads < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :squads do |t| 4 | t.string :name 5 | t.text :description 6 | t.belongs_to :leader, references: :users 7 | t.integer :minimum 8 | t.integer :maximum 9 | t.string :skill_level 10 | t.text :activities 11 | t.text :end_condition 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20170520150128_create_squad_mentors.rb: -------------------------------------------------------------------------------- 1 | class CreateSquadMentors < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :squad_mentors do |t| 4 | t.belongs_to :squad, foreign_key: true 5 | t.belongs_to :user, foreign_key: true 6 | t.timestamps 7 | end 8 | add_index(:squad_mentors, [:squad_id, :user_id], unique: true) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170520180320_add_members_to_squads.rb: -------------------------------------------------------------------------------- 1 | class AddMembersToSquads < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :squad_members do |t| 4 | t.belongs_to :squad, foreign_key: true 5 | t.belongs_to :user, foreign_key: true 6 | t.timestamps 7 | end 8 | add_index(:squad_members, [:squad_id, :user_id], unique: true) 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /db/migrate/20170520232041_add_status_to_requests.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToRequests < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :requests, :status, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170613012405_create_events.rb: -------------------------------------------------------------------------------- 1 | class CreateEvents < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :events do |t| 4 | t.string :name 5 | t.text :description 6 | t.string :URL 7 | t.datetime :start_date 8 | t.datetime :end_date 9 | t.string :address1 10 | t.string :address2 11 | t.string :city 12 | t.string :state 13 | t.string :zip 14 | t.boolean :scholarship_available 15 | 16 | t.timestamps 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20170616161847_change_url_to_url.rb: -------------------------------------------------------------------------------- 1 | class ChangeUrlToUrl < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_column :events, :URL, :url 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170622013242_add_verified_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddVerifiedToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :verified, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170629014745_create_team_members.rb: -------------------------------------------------------------------------------- 1 | class CreateTeamMembers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :team_members do |t| 4 | t.string :name 5 | t.string :role 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170701172700_create_resources.rb: -------------------------------------------------------------------------------- 1 | class CreateResources < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :resources do |t| 4 | t.string :name 5 | t.string :url 6 | t.string :category 7 | t.string :language 8 | t.boolean :paid 9 | t.text :notes 10 | t.integer :votes_count, default: 0, null: false 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20170701172846_create_votes.rb: -------------------------------------------------------------------------------- 1 | class CreateVotes < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :votes do |t| 4 | t.references :user, foreign_key: true 5 | t.references :resource, foreign_key: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170701194816_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 1) 2 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 3 | class ActsAsTaggableOnMigration < ActiveRecord::Migration[4.2]; end 4 | else 5 | class ActsAsTaggableOnMigration < ActiveRecord::Migration; end 6 | end 7 | ActsAsTaggableOnMigration.class_eval do 8 | def self.up 9 | create_table :tags do |t| 10 | t.string :name 11 | end 12 | 13 | create_table :taggings do |t| 14 | t.references :tag 15 | 16 | # You should make sure that the column created is 17 | # long enough to store the required class names. 18 | t.references :taggable, polymorphic: true 19 | t.references :tagger, polymorphic: true 20 | 21 | # Limit is created to prevent MySQL error on index 22 | # length for MyISAM table type: http://bit.ly/vgW2Ql 23 | t.string :context, limit: 128 24 | 25 | t.datetime :created_at 26 | end 27 | 28 | add_index :taggings, :tag_id 29 | add_index :taggings, [:taggable_id, :taggable_type, :context] 30 | end 31 | 32 | def self.down 33 | drop_table :taggings 34 | drop_table :tags 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /db/migrate/20170701194817_add_missing_unique_indices.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 2) 2 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 3 | class AddMissingUniqueIndices < ActiveRecord::Migration[4.2]; end 4 | else 5 | class AddMissingUniqueIndices < ActiveRecord::Migration; end 6 | end 7 | AddMissingUniqueIndices.class_eval do 8 | def self.up 9 | add_index :tags, :name, unique: true 10 | 11 | remove_index :taggings, :tag_id if index_exists?(:taggings, :tag_id) 12 | remove_index :taggings, [:taggable_id, :taggable_type, :context] 13 | add_index :taggings, 14 | [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], 15 | unique: true, name: 'taggings_idx' 16 | end 17 | 18 | def self.down 19 | remove_index :tags, :name 20 | 21 | remove_index :taggings, name: 'taggings_idx' 22 | 23 | add_index :taggings, :tag_id unless index_exists?(:taggings, :tag_id) 24 | add_index :taggings, [:taggable_id, :taggable_type, :context] 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20170701194818_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 3) 2 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 3 | class AddTaggingsCounterCacheToTags < ActiveRecord::Migration[4.2]; end 4 | else 5 | class AddTaggingsCounterCacheToTags < ActiveRecord::Migration; end 6 | end 7 | AddTaggingsCounterCacheToTags.class_eval do 8 | def self.up 9 | add_column :tags, :taggings_count, :integer, default: 0 10 | 11 | ActsAsTaggableOn::Tag.reset_column_information 12 | ActsAsTaggableOn::Tag.find_each do |tag| 13 | ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings) 14 | end 15 | end 16 | 17 | def self.down 18 | remove_column :tags, :taggings_count 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20170701194819_add_missing_taggable_index.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 4) 2 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 3 | class AddMissingTaggableIndex < ActiveRecord::Migration[4.2]; end 4 | else 5 | class AddMissingTaggableIndex < ActiveRecord::Migration; end 6 | end 7 | AddMissingTaggableIndex.class_eval do 8 | def self.up 9 | add_index :taggings, [:taggable_id, :taggable_type, :context] 10 | end 11 | 12 | def self.down 13 | remove_index :taggings, [:taggable_id, :taggable_type, :context] 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20170701194820_change_collation_for_tag_names.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 5) 2 | # This migration is added to circumvent issue #623 and have special characters 3 | # work properly 4 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 5 | class ChangeCollationForTagNames < ActiveRecord::Migration[4.2]; end 6 | else 7 | class ChangeCollationForTagNames < ActiveRecord::Migration; end 8 | end 9 | ChangeCollationForTagNames.class_eval do 10 | def up 11 | if ActsAsTaggableOn::Utils.using_mysql? 12 | execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;") 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20170701194821_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from acts_as_taggable_on_engine (originally 6) 2 | if ActiveRecord.gem_version >= Gem::Version.new('5.0') 3 | class AddMissingIndexesOnTaggings < ActiveRecord::Migration[4.2]; end 4 | else 5 | class AddMissingIndexesOnTaggings < ActiveRecord::Migration; end 6 | end 7 | AddMissingIndexesOnTaggings.class_eval do 8 | def change 9 | add_index :taggings, :tag_id unless index_exists? :taggings, :tag_id 10 | add_index :taggings, :taggable_id unless index_exists? :taggings, :taggable_id 11 | add_index :taggings, :taggable_type unless index_exists? :taggings, :taggable_type 12 | add_index :taggings, :tagger_id unless index_exists? :taggings, :tagger_id 13 | add_index :taggings, :context unless index_exists? :taggings, :context 14 | 15 | unless index_exists? :taggings, [:tagger_id, :tagger_type] 16 | add_index :taggings, [:tagger_id, :tagger_type] 17 | end 18 | 19 | unless index_exists? :taggings, [:taggable_id, :taggable_type, :tagger_id, :context], name: 'taggings_idy' 20 | add_index :taggings, [:taggable_id, :taggable_type, :tagger_id, :context], name: 'taggings_idy' 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /db/migrate/20170720215043_add_state_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddStateToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :state, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170723213338_add_more_attributes_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddMoreAttributesToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :address_1, :string 4 | add_column :users, :address_2, :string 5 | add_column :users, :city, :string 6 | add_column :users, :username, :string 7 | add_column :users, :volunteer, :boolean, default: false 8 | add_column :users, :branch_of_service, :string 9 | add_column :users, :years_of_service, :float 10 | add_column :users, :pay_grade, :string 11 | add_column :users, :military_occupational_specialty, :string 12 | add_column :users, :github, :string 13 | add_column :users, :twitter, :string 14 | add_column :users, :linkedin, :string 15 | add_column :users, :employment_status, :string 16 | add_column :users, :education, :string 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20170802201510_create_scholarships.rb: -------------------------------------------------------------------------------- 1 | class CreateScholarships < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :scholarships do |t| 4 | t.string :name 5 | t.text :description 6 | t.string :location 7 | t.text :terms 8 | t.datetime :start_date 9 | t.datetime :end_date 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20170802202307_create_applications.rb: -------------------------------------------------------------------------------- 1 | class CreateApplications < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :applications do |t| 4 | t.text :reason 5 | t.boolean :terms_accpeted 6 | t.references :user, foreign_key: true 7 | t.references :scholarship, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20170803215307_rename_applications_to_scholarship_applications.rb: -------------------------------------------------------------------------------- 1 | class RenameApplicationsToScholarshipApplications < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_table :applications, :scholarship_applications 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170803223854_rename_start_date_to_open_time.rb: -------------------------------------------------------------------------------- 1 | class RenameStartDateToOpenTime < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_column :scholarships, :start_date, :open_time 4 | rename_column :scholarships, :end_date, :close_time 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170806214216_add_additional_fields_for_profiles_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdditionalFieldsForProfilesToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :company_role, :string 4 | add_column :users, :company_name, :string 5 | add_column :users, :education_level, :string 6 | add_column :users, :interests, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20170807033541_create_git_hub_users.rb: -------------------------------------------------------------------------------- 1 | class CreateGitHubUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :git_hub_users do |t| 4 | t.integer :user_id, index: true 5 | t.string :git_hub_login 6 | t.string :avatar_url 7 | t.string :api_url 8 | t.string :html_url 9 | t.integer :git_hub_id 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20170807033815_create_git_hub_statistics.rb: -------------------------------------------------------------------------------- 1 | class CreateGitHubStatistics < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :git_hub_statistics do |t| 4 | t.references :git_hub_user, foreign_key: true 5 | t.string :source_id 6 | t.string :source_type 7 | t.string :state 8 | t.integer :additions 9 | t.integer :deletions 10 | t.string :repository 11 | t.string :url 12 | t.string :title 13 | t.string :number 14 | t.date :completed_on 15 | 16 | t.timestamps 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20170809164559_add_scholarship_info_field_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddScholarshipInfoFieldToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :scholarship_info, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170813163239_add_columns_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddColumnsToEvents < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :events, :source_id, :string, index: true, unique: true 4 | add_column :events, :source_type, :string, index: true 5 | add_column :events, :source_updated, :datetime 6 | add_column :events, :group, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20170821193457_change_scholarship_application.rb: -------------------------------------------------------------------------------- 1 | class ChangeScholarshipApplication < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_column :scholarship_applications, :terms_accpeted, :terms_accepted 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170903130609_add_index_to_source_events.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToSourceEvents < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :events, :source_type 4 | add_index :events, :source_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170920093119_create_code_schools.rb: -------------------------------------------------------------------------------- 1 | class CreateCodeSchools < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :code_schools do |t| 4 | t.string :name 5 | t.string :url 6 | t.string :logo 7 | t.boolean :full_time 8 | t.boolean :hardware_included 9 | t.boolean :has_online 10 | t.boolean :online_only 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20170920093217_create_locations.rb: -------------------------------------------------------------------------------- 1 | class CreateLocations < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :locations do |t| 4 | t.boolean :va_accepted 5 | t.string :address1 6 | t.string :address2 7 | t.string :city 8 | t.string :state 9 | t.integer :zip 10 | t.references :code_school, foreign_key: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20170921055331_add_notes_to_code_schools.rb: -------------------------------------------------------------------------------- 1 | class AddNotesToCodeSchools < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :code_schools, :notes, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171013230050_devise_create_admin_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateAdminUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :admin_users do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.inet :current_sign_in_ip 20 | t.inet :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps null: false 35 | end 36 | 37 | add_index :admin_users, :email, unique: true 38 | add_index :admin_users, :reset_password_token, unique: true 39 | # add_index :admin_users, :confirmation_token, unique: true 40 | # add_index :admin_users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20171013230053_create_active_admin_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateActiveAdminComments < ActiveRecord::Migration::Current 2 | def self.up 3 | create_table :active_admin_comments do |t| 4 | t.string :namespace 5 | t.text :body 6 | t.references :resource, polymorphic: true 7 | t.references :author, polymorphic: true 8 | t.timestamps 9 | end 10 | add_index :active_admin_comments, [:namespace] 11 | 12 | end 13 | 14 | def self.down 15 | drop_table :active_admin_comments 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20171022215418_drop_squads.rb: -------------------------------------------------------------------------------- 1 | class DropSquads < ActiveRecord::Migration[5.0] 2 | def change 3 | drop_table :squad_members 4 | drop_table :squad_mentors 5 | drop_table :squads 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180129223616_add_group_to_team_members.rb: -------------------------------------------------------------------------------- 1 | class AddGroupToTeamMembers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :team_members, :description, :text 4 | add_column :team_members, :group, :string 5 | add_column :team_members, :image_src, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180204235116_add_mooc_to_code_schools.rb: -------------------------------------------------------------------------------- 1 | class AddMoocToCodeSchools < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :code_schools, :mooc, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180401034441_create_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateRoles < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :roles do |t| 4 | t.string :title 5 | 6 | t.timestamps 7 | end 8 | 9 | add_reference :users, :role, index: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20180408172532_add_role_id_to_admin_user.rb: -------------------------------------------------------------------------------- 1 | class AddRoleIdToAdminUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_reference :admin_users, :role, index: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180416024105_add_email_to_team_member.rb: -------------------------------------------------------------------------------- 1 | class AddEmailToTeamMember < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :team_members, :email, :string, limit: 255 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180420192231_add_is_partner_to_code_schools.rb: -------------------------------------------------------------------------------- 1 | class AddIsPartnerToCodeSchools < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :code_schools, :is_partner, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180422030623_create_slack_user.rb: -------------------------------------------------------------------------------- 1 | class CreateSlackUser < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :slack_users do |t| 4 | t.string :slack_id, index: true 5 | t.string :slack_name 6 | t.string :slack_real_name 7 | t.string :slack_display_name 8 | t.string :slack_email, index: true 9 | t.integer :user_id, index: true 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20180422031009_drop_user_slack_id.rb: -------------------------------------------------------------------------------- 1 | class DropUserSlackId < ActiveRecord::Migration[5.0] 2 | def change 3 | remove_column :users, :slack_name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180430161218_add_rep_name_to_codeschool.rb: -------------------------------------------------------------------------------- 1 | class AddRepNameToCodeschool < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :code_schools, :rep_name, :string 4 | add_column :code_schools, :rep_email, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180711212702_add_militarystatus_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddMilitarystatusToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :military_status, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | web: 4 | build: . 5 | command: bundle exec puma -C config/puma.rb 6 | volumes: 7 | - .:/app 8 | volumes_from: 9 | - bundle 10 | ports: 11 | - "127.0.0.1:3000:3000" 12 | depends_on: 13 | - operationcode-psql 14 | - redis 15 | env_file: 16 | - .env 17 | 18 | bundle: 19 | image: operationcodebackend_web 20 | command: echo bundle data container 21 | volumes: 22 | - /bundle 23 | 24 | operationcode-psql: 25 | image: postgres 26 | env_file: 27 | - .env 28 | 29 | redis: 30 | image: redis:latest 31 | 32 | sidekiq: 33 | image: operationcodebackend_web 34 | command: bundle exec sidekiq -C config/sidekiq.yml.erb 35 | volumes: 36 | - .:/app 37 | volumes_from: 38 | - bundle 39 | depends_on: 40 | - operationcode-psql 41 | - redis 42 | env_file: 43 | - .env 44 | 45 | -------------------------------------------------------------------------------- /docs/data_models/request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/docs/data_models/request.png -------------------------------------------------------------------------------- /docs/data_models/squad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/docs/data_models/squad.png -------------------------------------------------------------------------------- /docs/setup/aws_vm_setup.md: -------------------------------------------------------------------------------- 1 | # Virtual Machine 2 | 3 | A virtual machine is defined as a computer file, typically called an image, that behaves like an actual computer. 4 | 5 | ## Create AWS VM 6 | (Using Ubuntu 16.04 on AWS) 7 | Make sure to open these ports in your security group 8 | - Port 22 9 | - Port 3000 10 | When the VM spins up, SSH into it. 11 | 12 | ## 13 | (from your VM) 14 | Fork https://github.com/operationcode/operationcode_backend 15 | 16 | ```bash 17 | git clone https://github.com/[YOUR-GITHUB-NAME]/operationcode_backend.git 18 | 19 | cd operationcode_backend 20 | 21 | git remote add upstream https://github.com/OperationCode/operationcode_backend.git 22 | 23 | sudo apt-get install make 24 | ``` 25 | 26 | ## Install Docker 27 | ```bash 28 | sudo apt-get update 29 | 30 | sudo apt-get install \ 31 | apt-transport-https \ 32 | ca-certificates \ 33 | curl \ 34 | software-properties-common 35 | 36 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 37 | 38 | sudo add-apt-repository \ 39 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ 40 | $(lsb_release -cs) \ 41 | stable" 42 | 43 | sudo apt-get update 44 | 45 | sudo apt-get install docker-ce 46 | ``` 47 | 48 | ## Install Docker Compose 49 | 50 | ```bash 51 | sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose 52 | sudo chmod +x /usr/local/bin/docker-compose 53 | ``` 54 | 55 | ## Setting up the Dev Environment 56 | ```bash 57 | sudo make setup 58 | ``` 59 | 60 | (if you want to try running the tests, run this command) 61 | 62 | ```bash 63 | sudo make test 64 | ``` 65 | 66 | Now navigate to http://:3000 and you should see the "You're running on Rails" page! 67 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/auto_generate_diagram.rake: -------------------------------------------------------------------------------- 1 | # NOTE: only doing this in development as some production environments (Heroku) 2 | # NOTE: are sensitive to local FS writes, and besides -- it's just not proper 3 | # NOTE: to have a dev-mode tool do its thing in production. 4 | 5 | RailsERD.load_tasks if Rails.env.development? 6 | -------------------------------------------------------------------------------- /lib/tasks/meetup.rake: -------------------------------------------------------------------------------- 1 | namespace :meetup do 2 | desc 'Rake task to get Meetup API data' 3 | task :fetch => :environment do 4 | begin 5 | Meetup.new.add_events_to_database! 6 | rescue => e 7 | Rails.logger.error "When fetching and saving Meetup events, experienced this error: #{e}" 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/tasks/resource.rake: -------------------------------------------------------------------------------- 1 | namespace :resource do 2 | desc "Initially seeds the database with OC's resources" 3 | task initial_seeding: :environment do 4 | config = Rails.root + 'config/resources.yml' 5 | resources = YAML.load_file(config) 6 | 7 | resources.each do |resource| 8 | begin 9 | Resource.create!( 10 | name: resource['name'], 11 | url: resource['url'], 12 | category: resource['category'], 13 | language: resource['language'], 14 | paid: resource['paid'], 15 | notes: resource['notes'] 16 | ) 17 | rescue => e 18 | Rails.logger.debug "When creating a Resource record for #{resource}, experienced this error: #{e}" 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/tasks/roles.rake: -------------------------------------------------------------------------------- 1 | namespace :roles do 2 | desc 'Creates the initial set of roles' 3 | task initial_set: :environment do 4 | ['super_admin', 'admin', 'board_member'].each do |role| 5 | begin 6 | Role.find_or_create_by!(title: role) 7 | rescue ActiveRecord::RecordInvalid => e 8 | p e.record.errors 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/tasks/schools_from_yml.rake: -------------------------------------------------------------------------------- 1 | namespace :schools do 2 | desc 'Reads from the ./config/code_schools.yml and creates records in the database.' 3 | task populate: :environment do 4 | schools = YAML::load_file(File.join('./config', 'code_schools.yml')) 5 | schools.each do |school| 6 | begin 7 | object = CodeSchool.create!( 8 | name: school['name'], 9 | url: school['url'], 10 | logo: school['logo'], 11 | full_time: school['full_time'], 12 | hardware_included: school['hardware_included'], 13 | has_online: school['has_online'], 14 | online_only: school['online_only'], 15 | notes: school['notes'] 16 | ) 17 | school['locations'].each do |location| 18 | object.locations.create!( 19 | va_accepted: location['va_accepted'], 20 | address1: location['address1'], 21 | address2: location['address2'], 22 | city: location['city'], 23 | state: location['state'], 24 | zip: location['zip'] 25 | ) 26 | end 27 | rescue StandardError => e 28 | message = "Failed to create CodeSchool: #{e}" 29 | 30 | puts message 31 | Rails.logger.error message 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/tasks/seed_members.rake: -------------------------------------------------------------------------------- 1 | namespace :seed_team_members do 2 | desc 'Seed team members' 3 | task all: :environment do 4 | SeedTeamMembers.seed_all 5 | end 6 | 7 | task board: :environment do 8 | SeedTeamMembers.seed_board 9 | end 10 | 11 | task team: :environment do 12 | SeedTeamMembers.seed_team 13 | end 14 | 15 | task reseed_all: :environment do 16 | SeedTeamMembers.clean_seed 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/tasks/test.rake: -------------------------------------------------------------------------------- 1 | namespace :test do 2 | task :policies => 'test:prepare' do 3 | $: << 'test' 4 | Minitest.rake_run(['test/policies']) 5 | end 6 | end 7 | 8 | Rake::Task['test:run'].enhance ['test:policies'] 9 | -------------------------------------------------------------------------------- /lib/tasks/update_is_partner_for_code_schools.rake: -------------------------------------------------------------------------------- 1 | namespace :is_partner do 2 | desc 'Updates column is_partner for code schools' 3 | task code_schools: :environment do 4 | is_partner_schools = [ 5 | 'Bloc', 6 | 'Coder Camps', 7 | 'Code Platoon', 8 | 'The Flatiron School', 9 | 'Fullstack Academy', 10 | 'Launch School', 11 | 'Sabio', 12 | 'Startup Institute', 13 | 'Thinkful' 14 | ] 15 | 16 | schools = CodeSchool.where(name: is_partner_schools) 17 | 18 | schools.each do |school| 19 | next if school.is_partner? 20 | school.is_partner = true 21 | school.save 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/tasks/update_sabio.rake: -------------------------------------------------------------------------------- 1 | namespace :update do 2 | desc 'Updates the Sabio.jpg to Sabio.png' 3 | task sabio: :environment do 4 | school = CodeSchool.find_by(name: 'Sabio') 5 | logo = school.logo 6 | logo[-3..-1] = 'png' 7 | school.save 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/tasks/update_schools_logo.rake: -------------------------------------------------------------------------------- 1 | namespace :update do 2 | desc 'Update code schools logo from github assets to AWS S3' 3 | task school_logos: :environment do 4 | schools = CodeSchool.all 5 | schools.each do |school| 6 | logo = school.logo 7 | if logo.include? 'https://raw.githubusercontent.com/OperationCode/operationcode_frontend/master/src/images' 8 | new_logo = logo.sub('https://raw.githubusercontent.com/OperationCode/operationcode_frontend/master/src/images', 'https://s3.amazonaws.com/operationcode-assets') 9 | 10 | school.logo = new_logo 11 | school.save 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/log/.keep -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/cassettes/airtable/client/post_record_successful.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.airtable.com/v0/some_random_id/Mentor%20Request 6 | body: 7 | encoding: UTF-8 8 | string: '{"fields":{"Slack User":"test_case_1","Service":["rec891lUSSaXM4qGC"],"Email":"test@example.com","Skillsets":["Java"],"Additional 9 | Details":"Some test description.","Mentor Requested":["recqeVhDDJU5cY8TX"]}}' 10 | headers: 11 | Authorization: 12 | - Bearer some_random_key 13 | Content-Type: 14 | - application/json 15 | response: 16 | status: 17 | code: 200 18 | message: OK 19 | headers: 20 | Access-Control-Allow-Headers: 21 | - authorization,content-length,content-type,user-agent,x-airtable-application-id,x-airtable-user-agent,x-api-version,x-requested-with 22 | Access-Control-Allow-Methods: 23 | - DELETE,GET,OPTIONS,PATCH,POST,PUT 24 | Access-Control-Allow-Origin: 25 | - "*" 26 | Content-Type: 27 | - application/json; charset=utf-8 28 | Date: 29 | - Mon, 13 Aug 2018 17:28:11 GMT 30 | Etag: 31 | - W/"138-KKUjxVDOWd7tv9LgEXtE8ftGIZo" 32 | Server: 33 | - Tengine 34 | Vary: 35 | - Accept-Encoding 36 | Content-Length: 37 | - '312' 38 | Connection: 39 | - keep-alive 40 | body: 41 | encoding: UTF-8 42 | string: '{"id":"recurURSzp8YEBXLA","fields":{"Slack User":"test_case_1","Service":["rec891lUSSaXM4qGC"],"Email":"test@example.com","Skillsets":["Java"],"Additional 43 | Details":"Some test description.","Mentor Requested":["recqeVhDDJU5cY8TX"],"Created 44 | At":"2018-08-13T17:28:11.000Z"},"createdTime":"2018-08-13T17:28:11.032Z"}' 45 | http_version: 46 | recorded_at: Mon, 13 Aug 2018 17:28:11 GMT 47 | recorded_with: VCR 3.0.3 48 | -------------------------------------------------------------------------------- /test/cassettes/airtable/mentorship/post_multiple_successful.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.airtable.com/v0/some_random_id/Mentor%20Request 6 | body: 7 | encoding: UTF-8 8 | string: '{"fields":{"Slack User":"test_case_1","Email":"test@example.com","Service":["rec891lUSSaXM4qGC"],"Skillsets":["Java","Study 9 | Help","Web Development (Front-end)"],"Additional Details":"Some test description.","Mentor 10 | Requested":["recqeVhDDJU5cY8TX"]}}' 11 | headers: 12 | Authorization: 13 | - Bearer some_random_key 14 | Content-Type: 15 | - application/json 16 | response: 17 | status: 18 | code: 200 19 | message: OK 20 | headers: 21 | Access-Control-Allow-Headers: 22 | - authorization,content-length,content-type,user-agent,x-airtable-application-id,x-airtable-user-agent,x-api-version,x-requested-with 23 | Access-Control-Allow-Methods: 24 | - DELETE,GET,OPTIONS,PATCH,POST,PUT 25 | Access-Control-Allow-Origin: 26 | - "*" 27 | Content-Type: 28 | - application/json; charset=utf-8 29 | Date: 30 | - Mon, 13 Aug 2018 17:20:42 GMT 31 | Etag: 32 | - W/"163-M/HK047xTstzRUEb5B4PTv0RDuQ" 33 | Server: 34 | - Tengine 35 | Vary: 36 | - Accept-Encoding 37 | Content-Length: 38 | - '355' 39 | Connection: 40 | - keep-alive 41 | body: 42 | encoding: UTF-8 43 | string: '{"id":"recqPcCumJ82y8B93","fields":{"Slack User":"test_case_1","Email":"test@example.com","Service":["rec891lUSSaXM4qGC"],"Skillsets":["Web 44 | Development (Front-end)","Java","Study Help"],"Additional Details":"Some test 45 | description.","Mentor Requested":["recqeVhDDJU5cY8TX"],"Created At":"2018-08-13T17:20:42.000Z"},"createdTime":"2018-08-13T17:20:42.595Z"}' 46 | http_version: 47 | recorded_at: Mon, 13 Aug 2018 17:20:42 GMT 48 | recorded_with: VCR 3.0.3 49 | -------------------------------------------------------------------------------- /test/cassettes/airtable/mentorship/post_successful.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.airtable.com/v0/some_random_id/Mentor%20Request 6 | body: 7 | encoding: UTF-8 8 | string: '{"fields":{"Slack User":"test_case_1","Email":"agreatperson@email.com","Service":["rec891lUSSaXM4qGC"],"Skillsets":["Java"],"Additional 9 | Details":"Some test description.","Mentor Requested":["recqeVhDDJU5cY8TX"]}}' 10 | headers: 11 | Authorization: 12 | - Bearer some_random_key 13 | Content-Type: 14 | - application/json 15 | response: 16 | status: 17 | code: 200 18 | message: OK 19 | headers: 20 | Access-Control-Allow-Headers: 21 | - authorization,content-length,content-type,user-agent,x-airtable-application-id,x-airtable-user-agent,x-api-version,x-requested-with 22 | Access-Control-Allow-Methods: 23 | - DELETE,GET,OPTIONS,PATCH,POST,PUT 24 | Access-Control-Allow-Origin: 25 | - "*" 26 | Content-Type: 27 | - application/json; charset=utf-8 28 | Date: 29 | - Mon, 13 Aug 2018 15:22:08 GMT 30 | Etag: 31 | - W/"13e-NrjauUnRwG+w1/mACLHDW1BJjs0" 32 | Server: 33 | - Tengine 34 | Vary: 35 | - Accept-Encoding 36 | Content-Length: 37 | - '318' 38 | Connection: 39 | - keep-alive 40 | body: 41 | encoding: UTF-8 42 | string: '{"id":"recMAtFD28bfgdvjg","fields":{"Slack User":"test_case_1","Email":"agreatperson@email.com","Service":["rec891lUSSaXM4qGC"],"Skillsets":["Java"],"Additional 43 | Details":"Some test description.","Mentor Requested":["recqeVhDDJU5cY8TX"],"Created 44 | At":"2018-08-13T15:22:08.000Z"},"createdTime":"2018-08-13T15:22:08.476Z"}' 45 | http_version: 46 | recorded_at: Mon, 13 Aug 2018 15:22:08 GMT 47 | recorded_with: VCR 3.0.3 48 | -------------------------------------------------------------------------------- /test/cassettes/slack_service/client/invite_failure.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: http://pybot.operationcode.org:8080/pybot/api/v1/slack/invite 6 | body: 7 | encoding: UTF-8 8 | string: '{"email":"test@example.com"}' 9 | headers: 10 | Authorization: 11 | - Bearer XXXX 12 | response: 13 | status: 14 | code: 403 15 | message: Forbidden 16 | headers: 17 | Date: 18 | - Tue, 23 Apr 2019 03:47:09 GMT 19 | Content-Type: 20 | - application/octet-stream 21 | Content-Length: 22 | - '0' 23 | Connection: 24 | - keep-alive 25 | Server: 26 | - Python/3.7 aiohttp/3.5.4 27 | body: 28 | encoding: UTF-8 29 | string: '' 30 | http_version: 31 | recorded_at: Tue, 23 Apr 2019 03:47:09 GMT 32 | recorded_with: VCR 4.0.0 33 | -------------------------------------------------------------------------------- /test/cassettes/slack_service/client/invite_success.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: http://pybot.operationcode.org:8080/pybot/api/v1/slack/invite 6 | body: 7 | encoding: UTF-8 8 | string: '{"email":"test@example.com"}' 9 | headers: 10 | Authorization: 11 | - Bearer some_random_key 12 | response: 13 | status: 14 | code: 200 15 | message: OK 16 | headers: 17 | Date: 18 | - Mon, 22 Apr 2019 23:34:40 GMT 19 | Content-Type: 20 | - text/plain; charset=utf-8 21 | Content-Length: 22 | - '12' 23 | Connection: 24 | - keep-alive 25 | Server: 26 | - Python/3.7 aiohttp/3.5.4 27 | body: 28 | encoding: UTF-8 29 | string: '{"ok": true}' 30 | http_version: 31 | recorded_at: Mon, 22 Apr 2019 23:34:40 GMT 32 | recorded_with: VCR 4.0.0 33 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/api/v1/email_list_recipients_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::EmailListRecipientsControllerTest < ActionDispatch::IntegrationTest 4 | def setup 5 | Sidekiq::Testing.fake! 6 | end 7 | 8 | def teardown 9 | Sidekiq::Worker.clear_all 10 | end 11 | 12 | test ":create with a valid email address renders the guest's email address and guest: true" do 13 | post api_v1_email_list_recipients_path, params: { email: valid_email }, as: :json 14 | 15 | assert JSON.parse(response.body) == { 'email' => valid_email, 'guest' => true } 16 | end 17 | 18 | test ':create with a valid email address, responds with a status of 201 (created)' do 19 | post api_v1_email_list_recipients_path, params: { email: valid_email }, as: :json 20 | 21 | assert response.status == 201 22 | end 23 | 24 | test ':create with a valid email address calls the AddGuestToSendGridJob' do 25 | post api_v1_email_list_recipients_path, params: { email: valid_email }, as: :json 26 | 27 | assert_equal 1, AddGuestToSendGridJob.jobs.length 28 | assert_equal [valid_email], AddGuestToSendGridJob.jobs.first['args'] 29 | end 30 | 31 | test ':create with a invalid email address renders an error message' do 32 | post api_v1_email_list_recipients_path, params: { email: invalid_email }, as: :json 33 | 34 | assert JSON.parse(response.body) == { 'errors' => "Invalid email address: #{invalid_email}" } 35 | end 36 | 37 | test ':create with an invalid email address does not call the AddGuestToSendGridJob' do 38 | 0.times { AddGuestToSendGridJob.expects(:perform_async) } 39 | 40 | post api_v1_email_list_recipients_path, params: { email: invalid_email }, as: :json 41 | end 42 | end 43 | 44 | def valid_email 45 | 'john@gmail.com' 46 | end 47 | 48 | def invalid_email 49 | 'johngmail.com' 50 | end 51 | -------------------------------------------------------------------------------- /test/controllers/api/v1/mentors_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::MentorsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test 'users can get list of mentors' do 6 | skip 7 | user = create(:user) 8 | headers = authorization_headers(user) 9 | create_list(:mentor, 2) 10 | get api_v1_mentors_url, headers: headers, as: :json 11 | assert_equal 2, response.parsed_body.count 12 | end 13 | 14 | test 'users can show a mentor' do 15 | user = create(:user) 16 | headers = authorization_headers(user) 17 | mentor = create(:mentor) 18 | 19 | get api_v1_mentor_url(mentor), headers: headers, as: :json 20 | assert_equal mentor.bio, response.parsed_body['bio'] 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /test/controllers/api/v1/services_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::ServicesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test 'should get list of all services' do 6 | skip 7 | user = create(:user) 8 | headers = authorization_headers(user) 9 | create_list(:service, 2) 10 | get api_v1_services_url, headers: headers, as: :json 11 | assert_equal 2, response.parsed_body.count 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/controllers/api/v1/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::ServicesControllerTest < ActionDispatch::IntegrationTest 4 | def setup 5 | @user = create(:user, password: 'pass123') 6 | @user_params = { 7 | user: { 8 | email: @user.email, 9 | password: 'pass123' 10 | } 11 | } 12 | end 13 | 14 | test 'returns JWT token for succesfull login' do 15 | post api_v1_sessions_url, params: @user_params, as: :json 16 | json = response.parsed_body 17 | refute_nil json['token'] 18 | end 19 | 20 | test 'returns user info' do 21 | post api_v1_sessions_url, params: @user_params, as: :json 22 | json = response.parsed_body 23 | assert_equal @user.id, json['user']['id'] 24 | assert_equal @user.first_name, json['user']['first_name'] 25 | assert_equal @user.last_name, json['user']['last_name'] 26 | refute json['user']['mentor'] 27 | end 28 | 29 | test 'it returns a redirect_to path to the users profile for a regular login' do 30 | post api_v1_sessions_url, params: @user_params, as: :json 31 | json = response.parsed_body 32 | assert_equal '/profile', json['redirect_to'] 33 | end 34 | 35 | def sso_params 36 | { 37 | sso: 'bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==', 38 | sig: 'd6bc820029617d00b54f1bb0cf117a91a037b284cbd7d2832c95ef8371062eb1' 39 | } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/controllers/api/v1/slack_users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::SlackUsersControllerTest < ActionDispatch::IntegrationTest 4 | test ':access confirms the passed token is equal to the secrets.py_bot_auth_key' do 5 | token = JsonWebToken.encode({ key: Rails.application.secrets.py_bot_auth_key }) 6 | 7 | get access_api_v1_slack_users_url, 8 | headers: auth_headers(token), 9 | as: :json 10 | 11 | body = JSON.parse(response.body) 12 | 13 | assert_equal response.status, 200 14 | assert_equal body, { 'message' => 'You have access!' } 15 | end 16 | 17 | test ':access returns a 401 with an incorrect token' do 18 | invalid_token = JsonWebToken.encode({ key: 'an invalid token' }) 19 | 20 | get access_api_v1_slack_users_url, 21 | headers: auth_headers(invalid_token), 22 | as: :json 23 | 24 | body = JSON.parse(response.body) 25 | 26 | assert_equal response.status, 401 27 | assert_equal body, { 'errors' => ['Auth token is invalid'] } 28 | end 29 | 30 | test ':access returns a 401 with an expired token' do 31 | expired_token = JsonWebToken.encode({ key: 'an expired token' }, 0) 32 | 33 | get access_api_v1_slack_users_url, 34 | headers: auth_headers(expired_token), 35 | as: :json 36 | 37 | body = JSON.parse(response.body) 38 | 39 | assert_equal response.status, 401 40 | assert_equal body, { 'errors' => ['Auth token has expired'] } 41 | end 42 | 43 | test ':access returns a 401 with a malformed token' do 44 | malformed_token = 'a malformed token' 45 | 46 | get access_api_v1_slack_users_url, 47 | headers: auth_headers(malformed_token), 48 | as: :json 49 | 50 | body = JSON.parse(response.body) 51 | 52 | assert_equal response.status, 401 53 | assert_equal body, { 'errors' => ['Invalid auth token'] } 54 | end 55 | end 56 | 57 | def auth_headers(token) 58 | { 'Authorization': "bearer #{token}" } 59 | end 60 | -------------------------------------------------------------------------------- /test/controllers/api/v1/social_users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::SocialUsersControllerTest < ActionDispatch::IntegrationTest 4 | test 'show returns /profile if the user is already registered' do 5 | bob = create(:user, first_name: 'Bob', last_name: 'Belcher', email: 'bobs@burgers.com', zip: '08260', password: 'Archer' ) 6 | params = { params: { email: 'bobs@burgers.com'} } 7 | 8 | get api_v1_social_users_url(params), as: :json 9 | json = response.parsed_body 10 | 11 | assert_equal '/login', json['redirect_to'] 12 | end 13 | 14 | test 'show returns /social_login if the user is not registered' do 15 | params = { params: { email: 'bobs@burgers.com'} } 16 | 17 | get api_v1_social_users_url(params), as: :json 18 | json = response.parsed_body 19 | 20 | assert_equal '/social_login', json['redirect_to'] 21 | end 22 | 23 | test 'create returns token, user and redirect path /profile when the user is already registered' do 24 | params = { user: { first_name: 'James', last_name: 'Kirk', email: 'kirk@starfleet.gov', zip: '94965', password: 'XOSpock'} } 25 | create(:user, params[:user]) 26 | 27 | post api_v1_social_users_url, params: params, as: :json 28 | json = response.parsed_body 29 | 30 | assert_equal '/profile', json['redirect_to'] 31 | assert_equal params[:user][:first_name], json['user']['first_name'] 32 | assert_equal params[:user][:last_name], json['user']['last_name'] 33 | assert_equal params[:user][:email], json['user']['email'] 34 | assert_equal params[:user][:zip], json['user']['zip'] 35 | refute_nil json['token'] 36 | end 37 | 38 | test 'create returns token, user and redirect path /social-login when the user is not registered' do 39 | params = { user: { first_name: 'Jean Luc',last_name: 'Picard',email: 'picard@tng.com',zip: '94965',password: 'Engage'} } 40 | 41 | post api_v1_social_users_url, params: params, as: :json 42 | json = response.parsed_body 43 | 44 | assert_equal '/signup-info', json['redirect_to'] 45 | assert_equal params[:user][:first_name], json['user']['first_name'] 46 | assert_equal params[:user][:last_name], json['user']['last_name'] 47 | assert_equal params[:user][:email], json['user']['email'] 48 | assert_equal params[:user][:zip], json['user']['zip'] 49 | refute_nil json['token'] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/controllers/api/v1/tags_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::TagsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | resource = create :resource 6 | 7 | resource.tag_list.add('books', 'videos') 8 | resource.save 9 | end 10 | 11 | test ':index endpoint returns JSON list of Tags' do 12 | get api_v1_tags_url, as: :json 13 | 14 | assert_equal response.status, 200 15 | ['books', 'videos'].each do |tag_name| 16 | assert_equal true, response.parsed_body.any? { |tag| tag['name'] == tag_name } 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/controllers/api/v1/votes_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::VotesControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = create :user 6 | @headers = authorization_headers @user 7 | @resource = create :resource 8 | end 9 | 10 | test ':create endpoint creates a new Vote' do 11 | params = { 12 | user_id: @user.id, 13 | resource_id: @resource.id 14 | } 15 | 16 | post api_v1_resource_votes_url(@resource), headers: @headers, params: params, as: :json 17 | 18 | vote = Vote.first 19 | assert_equal({ 'vote' => vote.id }, response.parsed_body) 20 | end 21 | 22 | test 'User cannot create more than one Vote for a given Resource' do 23 | new_resource = create :resource 24 | create :vote, user: @user, resource: new_resource 25 | vote = create :vote, user: @user, resource: @resource 26 | assert_equal 2, @user.votes.count 27 | 28 | params = { 29 | user_id: @user.id, 30 | resource_id: @resource.id 31 | } 32 | 33 | post api_v1_resource_votes_url(@resource), headers: @headers, params: params, as: :json 34 | 35 | assert_equal 422, response.status 36 | assert_equal({ 'errors' => 'Validation failed: User has already voted for this resource' }, response.parsed_body) 37 | assert_equal 2, @user.votes.count 38 | end 39 | 40 | test ':destroy endpoint destroys an existing Vote' do 41 | vote = create :vote, user: @user, resource: @resource 42 | 43 | params = { 44 | id: vote.id, 45 | user_id: vote.user_id, 46 | resource_id: vote.resource_id 47 | } 48 | 49 | delete api_v1_resource_vote_url(@resource, vote), headers: @headers, params: params, as: :json 50 | 51 | assert_equal response.status, 200 52 | assert_equal 0, Vote.count 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/factories/code_schools.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :code_school do 3 | name { Faker::Company.name } 4 | url { Faker::Internet.url } 5 | logo { Faker::Internet.url } 6 | full_time { Faker::Boolean.boolean } 7 | hardware_included { Faker::Boolean.boolean } 8 | has_online { Faker::Boolean.boolean } 9 | online_only { Faker::Boolean.boolean } 10 | notes { Faker::Lorem.paragraph } 11 | mooc false 12 | is_partner false 13 | rep_name { Faker::Name.name } 14 | rep_email { Faker::Internet.email } 15 | 16 | factory :code_school_with_locations do 17 | transient do 18 | locations_count { rand(1..15) } 19 | end 20 | 21 | after :create do |code_school, evaluator| 22 | create_list(:location, evaluator.locations_count, code_school: code_school) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/factories/events.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :event do 4 | name 'MyEvent' 5 | description { Faker::Hacker.say_something_smart } 6 | url { Faker::Internet.url } 7 | start_date { Faker::Time.forward(30, :morning) } 8 | end_date { Faker::Time.forward(30, :morning) } 9 | address1 { Faker::Address.street_address } 10 | address2 { Faker::Address.street_address } 11 | city { Faker::Address.city } 12 | source_type 'Meetup' 13 | source_id { Faker::Lorem.characters(7) } 14 | group { Faker::Lorem.characters(10) } 15 | 16 | trait :updated_in_the_future do 17 | source_updated {2.days.from_now} 18 | end 19 | 20 | trait :updated_today do 21 | source_updated { Date.today } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/factories/git_hub_statistics.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :git_hub_statistic do 3 | git_hub_user 4 | sequence(:source_id, 10000) { |n| n.to_s } 5 | source_type GitHubStatistic::PR 6 | repository 'operationcode_backend' 7 | url { Faker::Internet.url } 8 | title { Faker::Hacker.say_something_smart } 9 | state 'closed' 10 | completed_on { Faker::Date.between(5.years.ago, Date.yesterday) } 11 | 12 | trait :commit do 13 | source_type GitHubStatistic::COMMIT 14 | state nil 15 | end 16 | 17 | trait :issue do 18 | source_type GitHubStatistic::ISSUE 19 | state 'open' 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/factories/git_hub_users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :git_hub_user do 3 | git_hub_login 'oc coder' 4 | git_hub_id { Faker::Number.number(5).to_i } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/factories/locations.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :location do 3 | code_school { association(:code_school) || CodeSchool.last } 4 | va_accepted { Faker::Boolean.boolean } 5 | address1 { Faker::Address.street_address } 6 | address2 { Faker::Address.street_address } 7 | city { Faker::Address.city } 8 | state { Faker::Address.state } 9 | zip { Faker::Address.zip_code } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/factories/requests.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | 3 | factory :request do 4 | user 5 | association :requested_mentor, factory: :mentor 6 | details { Faker::Lorem.paragraph } 7 | language 'Javascript' 8 | status { ['Unassigned', 'Assigned', 'Contacted', 'Awaiting Response', 'Scheduled', 'Completed'].sample } 9 | service 10 | 11 | trait :claimed do 12 | association :assigned_mentor, factory: :mentor 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /test/factories/resources.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :resource do 3 | name 'Free Tech Books' 4 | url 'http://www.freetechbooks.com/' 5 | category 'Books' 6 | language 'Multiple' 7 | paid false 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/factories/roles.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :role do 3 | title 'standard_user' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/factories/scholarship_applications.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :scholarship_application do 3 | reason 'MyText' 4 | terms_accepted false 5 | association :user, factory: :user, verified: true 6 | association :scholarship, factory: :scholarship 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/factories/scholarships.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :scholarship do 3 | name 'MyString' 4 | description 'MyText' 5 | location 'MyString' 6 | terms 'MyText' 7 | open_time '2017-08-02 15:15:11' 8 | close_time '2017-08-02 15:15:11' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/factories/services.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :service do 3 | name { Faker::Company.name } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/factories/team_members.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :team_member do 3 | name 'John Smith' 4 | role 'Board Advisor' 5 | group TeamMember::BOARD_GROUP_NAME 6 | description 'Long and wonderful history of employment' 7 | image_src 'images/john_smith.jpg' 8 | end 9 | 10 | trait :board do 11 | group TeamMember::BOARD_GROUP_NAME 12 | end 13 | 14 | trait :executive do 15 | group TeamMember::EXECUTIVE_GROUP_NAME 16 | end 17 | 18 | trait :team do 19 | group TeamMember::TEAM_GROUP_NAME 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | email { Faker::Internet.email } 4 | zip '97201' 5 | first_name { Faker::Name.first_name } 6 | last_name { Faker::Name.last_name } 7 | latitude { Faker::Number.decimal(2, 5) } 8 | longitude { Faker::Number.decimal(2, 5) } 9 | password { Faker::Lorem.characters(32) } 10 | bio { Faker::Company.bs } 11 | timezone 'EST' 12 | mentor false 13 | verified false 14 | state { Faker::Address.state_abbr } 15 | 16 | factory :mentor do 17 | mentor true 18 | end 19 | 20 | trait :dynamic_zip do 21 | zip { Faker::Address.postcode } 22 | end 23 | 24 | trait :verified do 25 | verified true 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/factories/votes.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :vote do 3 | user 4 | resource 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/test/integration/.keep -------------------------------------------------------------------------------- /test/integration/events_create_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class EventsCreateTest < ActionDispatch::IntegrationTest 4 | setup do 5 | Event.delete_all 6 | end 7 | 8 | test 'valid event information saves' do 9 | assert_difference 'Event.count', 1 do 10 | create :event, :updated_today 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /test/jobs/add_guest_to_send_grid_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AddGuestToSendGridJobTest < ActiveSupport::TestCase 4 | test 'it adds the guest to send grid' do 5 | job = AddGuestToSendGridJob.new 6 | guest = SendGridClient::Guest.user(valid_email) 7 | 8 | SendGridClient.any_instance.expects(:add_user).with(guest) 9 | 10 | job.perform(valid_email) 11 | end 12 | end 13 | 14 | def valid_email 15 | 'john@gmail.com' 16 | end 17 | -------------------------------------------------------------------------------- /test/jobs/add_user_to_send_grid_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AddUserToSendGridJobTest < ActiveSupport::TestCase 4 | test 'it adds a user to send grid' do 5 | job = AddUserToSendGridJob.new 6 | user = FactoryGirl.create(:user) 7 | 8 | SendGridClient.any_instance.expects(:add_user).with(user) 9 | 10 | job.perform(user.id) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/jobs/meetup_member_sync_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MeetupMemberSyncJobTest < ActiveSupport::TestCase 4 | member = { 5 | 'email' => 'test@test.com', 6 | 'lat' => 36.900001525878906, 7 | 'lon' => -76.20999908447266, 8 | 'city' => 'Norfolk', 9 | 'state' => 'VA' 10 | } 11 | 12 | test 'it only updates nil fields on a found user' do 13 | job = MeetupMemberSyncJob.new 14 | user_id = FactoryGirl.create(:user, email: 'test@test.com', city: nil).id 15 | Meetup.any_instance.expects(:members_by_email).returns('test@test.com' => member) 16 | job.perform 17 | 18 | user = User.find(user_id) 19 | assert user.city == 'Norfolk' 20 | assert user.state != 'VA' 21 | end 22 | 23 | test 'it only updates users with matching emails' do 24 | job = MeetupMemberSyncJob.new 25 | FactoryGirl.create(:user, email: 'test@test.com', city: nil) 26 | user2 = FactoryGirl.create(:user, email: 'test2@test.com', city: nil) 27 | 28 | Meetup.any_instance.expects(:members_by_email).returns('test@test.com' => member) 29 | job.perform 30 | 31 | assert user2 == User.find(user2.id) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/lib/code_schools_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CodeSchoolsTest < ActiveSupport::TestCase 4 | test 'raises if config doesnt exist' do 5 | assert_raises { CodeSchools.all('/a/non/existent/path') } 6 | end 7 | 8 | test 'it returns schools' do 9 | c = CodeSchools.all(Rails.root + 'test/support/code_schools/test_config.yml') 10 | assert_kind_of Array, c 11 | listing = c.first 12 | assert_kind_of Array, listing['locations'] 13 | assert_equal 2, listing['locations'].count 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/lib/format_data_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FormatDataTest < ActiveSupport::TestCase 4 | test '.csv_to_array' do 5 | results = FormatData.csv_to_array 'tx' 6 | assert_equal ['TX'], results 7 | 8 | results = FormatData.csv_to_array 'tx, mn' 9 | assert_equal ['TX', 'MN'], results 10 | 11 | results = FormatData.csv_to_array '80202, 80126' 12 | assert_equal ['80202', '80126'], results 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/lib/git_hub/authentication_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GitHub::AuthenticationTest < ActiveSupport::TestCase 4 | setup do 5 | GitHubStatistic.delete_all 6 | GitHubUser.delete_all 7 | 8 | @options = { 9 | query: { 10 | per_page: 100, 11 | }, 12 | headers: { 13 | 'Accepts' => 'application/vnd.github.v3+json', 14 | 'User-Agent' => 'operationcode', 15 | }, 16 | } 17 | 18 | GitHub::Settings.stubs(:authentication_level).returns(GitHub::Authentication::O_AUTH_2_KEY_SECRET) 19 | 20 | @instance = GitHub::Authentication.new(@options) 21 | end 22 | 23 | test 'initialize constructs the expected variable' do 24 | assert_equal @options, @instance.options 25 | assert_equal GitHub::Authentication::O_AUTH_2_KEY_SECRET, @instance.auth_level 26 | end 27 | 28 | test '#set_options returns the passed in options when Rails.env.prod? is false' do 29 | assert @options, @instance.set_options 30 | end 31 | 32 | test '#set_options merges the authenticated OAUTH_KEY options hash when Rails.env.prod? is true' do 33 | Rails.env.stubs(:prod?).returns(true) 34 | 35 | assert_equal @instance.set_options, 36 | :query => 37 | { 38 | :client_id => 'some random id', 39 | :client_secret => 'some random key', 40 | :per_page => 100 41 | }, 42 | :headers => 43 | { 44 | 'Accepts' => 'application/vnd.github.v3+json', 45 | 'User-Agent' => 'operationcode' 46 | } 47 | end 48 | 49 | test '#set_options merges the authenticated OAUTH_TOKEN options hash when Rails.env.prod? is true' do 50 | GitHub::Settings.stubs(:authentication_level).returns(GitHub::Authentication::O_AUTH_2_TOKEN) 51 | Rails.env.stubs(:prod?).returns(true) 52 | 53 | instance = GitHub::Authentication.new(@options) 54 | assert_equal instance.set_options, 55 | :query => 56 | { 57 | :per_page => 100 58 | }, 59 | :headers => 60 | { 61 | 'Accepts' => 'application/vnd.github.v3+json', 62 | 'User-Agent' => 'operationcode', 63 | 'Authorization' => "Bearer #{GitHub::Settings.o_auth_2_token}" 64 | } 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /test/lib/git_hub/committer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GitHub::CommitterTest < ActiveSupport::TestCase 4 | setup do 5 | GitHubStatistic.delete_all 6 | GitHubUser.delete_all 7 | 8 | @user_attrs = { 9 | user_id: nil, 10 | login: 'jack', 11 | avatar_url: 'https://avatars3.githubusercontent.com/u/123456?v=4', 12 | api_url: 'https://api.github.com/users/jack', 13 | html_url: 'https://github.com/jack', 14 | } 15 | 16 | @stat_attrs = { 17 | source_id: '128533020', 18 | source_type: 'PullRequest', 19 | state: 'closed', 20 | additions: 100, 21 | deletions: 3, 22 | repository: 'operationcode', 23 | url: 'https://api.github.com/repos/OperationCode/operationcode/pulls/753', 24 | title: 'Fix this that and the other thing', 25 | number: '753', 26 | closed_on: '2017-7-02' 27 | } 28 | end 29 | 30 | test '.find_or_create_user! creates and returns non-existing GitHubUser' do 31 | assert GitHubUser.count == 0 32 | 33 | user = GitHub::Committer.find_or_create_user! @user_attrs 34 | 35 | assert_equal user, GitHubUser.first 36 | end 37 | 38 | test '.find_or_create_user! returns the existing matched user' do 39 | user = create :git_hub_user, git_hub_login: @user_attrs[:login] 40 | results = GitHub::Committer.find_or_create_user! @user_attrs.merge(id: user.git_hub_id) 41 | 42 | assert_equal user, results 43 | end 44 | 45 | test '.find_or_create_statistic! creates and returns non-existing GitHubStatistic' do 46 | assert GitHubStatistic.count == 0 47 | 48 | stat = GitHub::Committer.find_or_create_statistic!( 49 | @stat_attrs, 50 | @stat_attrs[:source_type], 51 | create(:git_hub_user).id 52 | ) 53 | 54 | assert_equal stat, GitHubStatistic.first 55 | end 56 | 57 | test '.find_or_create_statistic! returns the existing matched user' do 58 | user = create :git_hub_user 59 | stat = create( 60 | :git_hub_statistic, 61 | source_id: @stat_attrs[:source_id], 62 | source_type: @stat_attrs[:source_type] 63 | ) 64 | 65 | results = GitHub::Committer.find_or_create_statistic!( 66 | @stat_attrs.merge(id: stat.source_id), 67 | stat.source_type, 68 | user.git_hub_id 69 | ) 70 | 71 | assert_equal stat, results 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /test/lib/send_grid_client/guest_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SendGridClient::GuestTest < ActiveSupport::TestCase 4 | test '#user creates a user-like Struct, to represent an OC guest' do 5 | guest = SendGridClient::Guest.user(valid_email) 6 | 7 | assert guest.to_s.include?('struct') 8 | end 9 | 10 | test "#user creates an :id method that defaults to 'guest-id'" do 11 | guest = SendGridClient::Guest.user(valid_email) 12 | 13 | assert 'guest-id', guest.id 14 | end 15 | 16 | test '#user creates an :email method that is set to the passed email address' do 17 | guest = SendGridClient::Guest.user(valid_email) 18 | 19 | assert_equal valid_email, guest.email 20 | end 21 | 22 | test "#attributes returns JSON for the Struct's id, first_name, last_name, and email" do 23 | guest = SendGridClient::Guest.user(valid_email) 24 | 25 | assert_equal guest.attributes, 26 | 'id' => 'guest-id', 27 | 'email' => valid_email, 28 | 'first_name' => '', 29 | 'last_name' => '' 30 | end 31 | end 32 | 33 | def valid_email 34 | 'john@gmail.com' 35 | end 36 | -------------------------------------------------------------------------------- /test/lib/send_grid_client_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SendGridClientTest < ActiveSupport::TestCase 4 | test 'it adds a user to sendgrid' do 5 | user = FactoryGirl.build(:user) 6 | mock_sendgrid_client_for(user) 7 | assert SendGridClient.new.add_user(user) 8 | end 9 | 10 | # Sendgrid's gem uses a TON of chained methods. This mess is stubbing out all of them 11 | # while checking to make sure we get sane parameters 12 | def mock_sendgrid_client_for(user) 13 | mock_response = mock 14 | mock_response.expects(:status_code).returns('201').at_least_once 15 | mock_response.stubs(:body).returns('{ "error_count": 0, "persisted_recipients": [ "1234" ] }') 16 | 17 | mock_recipients = mock 18 | mock_recipients.expects(:post).with(request_body: ['first_name' => user.first_name, 'last_name' => user.last_name, 'email' => user.email]).returns(mock_response) 19 | 20 | mock_list_response = mock 21 | mock_list_response.stubs(:status_code).returns('201') 22 | mock_list_response.stubs(:body).returns(nil) 23 | 24 | mock_list_post = mock 25 | mock_list_post.stubs(:post).returns(mock_list_response) 26 | 27 | mock_recipient_id = mock 28 | mock_recipient_id.stubs(:_).with(['1234']).returns(mock_list_post) 29 | 30 | mock_list_id = mock 31 | mock_list_id.stubs(:recipients).returns(mock_recipient_id) 32 | 33 | mock_lists = mock 34 | mock_lists.stubs(:_).with(SendGridClient::LIST_ID).returns(mock_list_id) 35 | 36 | mock_contactdb = mock 37 | mock_contactdb.stubs(:recipients).returns(mock_recipients) 38 | mock_contactdb.stubs(:lists).returns(mock_lists) 39 | 40 | mock_client = mock 41 | mock_client.stubs(:contactdb).returns(mock_contactdb) 42 | 43 | SendGrid::API.any_instance.stubs(:client).returns(mock_client) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/lib/update_school_logos_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UpdateSchoolLogosTest < ActiveSupport::TestCase 4 | setup do 5 | Rails.application.load_tasks 6 | if !CodeSchool.exists? 7 | Rake::Task['schools:populate'].invoke 8 | end 9 | end 10 | 11 | test 'it asserts schools\' logos contain aws urls' do 12 | Rake::Task['update:school_logos'].invoke 13 | schools = CodeSchool.all 14 | assert_kind_of CodeSchool::ActiveRecord_Relation, schools 15 | schools.each do |school| 16 | logo = school.logo 17 | assert_match 'https://s3.amazonaws.com/operationcode-assets', logo 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/lib/users_by_location_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersByLocationTest < ActiveSupport::TestCase 4 | setup do 5 | tom = create :user 6 | sam = create :user 7 | 8 | tom.update_columns latitude: 39.763034, longitude: -104.961969, zip: '80205', state: 'CO' 9 | sam.update_columns latitude: 30.312601, longitude: -97.738591, zip: '78756', state: 'TX' 10 | end 11 | 12 | test '#count by state' do 13 | params = { state: 'CO' } 14 | results = UsersByLocation.new(params).count 15 | assert_equal 1, results 16 | 17 | params = { state: 'TX' } 18 | results = UsersByLocation.new(params).count 19 | assert_equal 1, results 20 | 21 | params = { state: 'CO, TX' } 22 | results = UsersByLocation.new(params).count 23 | assert_equal 2, results 24 | 25 | params = { state: 'co, tx' } 26 | results = UsersByLocation.new(params).count 27 | assert_equal 2, results 28 | end 29 | 30 | test '#count by zip' do 31 | params = { zip: '80205' } 32 | results = UsersByLocation.new(params).count 33 | assert_equal 1, results 34 | 35 | params = { zip: '78756' } 36 | results = UsersByLocation.new(params).count 37 | assert_equal 1, results 38 | 39 | params = { zip: '80205, 78756' } 40 | results = UsersByLocation.new(params).count 41 | assert_equal 2, results 42 | end 43 | 44 | test '#count by lat_long' do 45 | params = { lat_long: [39.763034, -104.961969] } 46 | results = UsersByLocation.new(params).count 47 | assert_equal 1, results 48 | 49 | params = { lat_long: [30.312601, -97.738591] } 50 | results = UsersByLocation.new(params).count 51 | assert_equal 1, results 52 | end 53 | end 54 | 55 | -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActiveSupport::TestCase 4 | include ActionMailer::TestHelper 5 | 6 | test 'renders the subject' do 7 | new_user = create(:user) 8 | leader = create(:user) 9 | mail = UserMailer.new_user_in_leader_area(leader, new_user).deliver_now 10 | assert_equal mail.subject, 'A new user has joined within 50 miles of you' 11 | end 12 | 13 | test 'renders the body' do 14 | new_user = create(:user) 15 | leader = create(:user) 16 | expected = <<~EMAIL_BODY 17 | #{CGI.escapeHTML(new_user.name)}, has joined. They're located within 50 miles of your chapter! 18 | Please email them at #{new_user.email} and welcome them, and consider providing info on the next local meetup. 19 | Let's encourage in-person involvement within Operation Code! 20 | EMAIL_BODY 21 | mail = UserMailer.new_user_in_leader_area(leader, new_user).deliver_now 22 | assert_equal mail.body.to_s, expected 23 | end 24 | 25 | test 'renders the recipients' do 26 | new_user = create(:user) 27 | leader = create(:user) 28 | mail = UserMailer.new_user_in_leader_area(leader, new_user).deliver_now 29 | assert_equal mail.to, [leader.email] 30 | end 31 | 32 | test 'enqueues actionmailer' do 33 | assert_enqueued_emails 1 do 34 | new_user = create(:user) 35 | leader = create(:user) 36 | UserMailer.new_user_in_leader_area(leader, new_user).deliver_later 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/test/models/.keep -------------------------------------------------------------------------------- /test/models/code_schools_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CodeSchoolTest < ActiveSupport::TestCase 4 | def setup 5 | @code_school = build :code_school 6 | end 7 | 8 | test 'is_partner should be present and default to false' do 9 | assert_not @code_school.is_partner 10 | end 11 | 12 | test 'rep_name should be present' do 13 | assert @code_school.rep_name.present? 14 | end 15 | 16 | test 'rep_email should be present' do 17 | assert @code_school.rep_email.present? 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/models/event_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class EventTest < ActiveSupport::TestCase 4 | 5 | ##Testing the validations 6 | def setup 7 | @event = build :event, :updated_today 8 | end 9 | 10 | test 'should be valid' do 11 | #Current event as described in setup should be valid 12 | assert @event.valid? 13 | end 14 | 15 | test 'name should be present' do 16 | #Confirm empty event name is not valid 17 | @event.name = ' ' 18 | assert_not @event.valid? 19 | end 20 | 21 | test 'start_date should be present' do 22 | @event.start_date = ' ' 23 | assert_not @event.valid? 24 | end 25 | 26 | test 'address1 should be present' do 27 | @event.address1 = ' ' 28 | assert_not @event.valid? 29 | end 30 | 31 | test 'city should be present' do 32 | @event.city = ' ' 33 | assert_not @event.valid? 34 | end 35 | 36 | test 'website validation should accept valid websites' do 37 | valid_websites = %w[http://www.somewebsite.com https://www.1234.com http://www.foobar.net] 38 | valid_websites.each do |valid_website| 39 | @event.url = valid_website 40 | assert @event.valid?, "#{valid_website.inspect} should be valid" 41 | end 42 | end 43 | 44 | test 'website validation should reject invalid urls' do 45 | bad_websites = %w[w.badwebsite.com www,stillbad.com abc.fail.com wwww.foobar.com] 46 | bad_websites.each do |bad_website| 47 | @event.url = bad_website 48 | assert_not @event.valid?, "#{bad_website.inspect} should not be valid" 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/models/request_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RequestTest < ActiveSupport::TestCase 4 | test 'the truth' do 5 | create(:request) 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /test/models/resource_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ResourceTest < ActiveSupport::TestCase 4 | setup do 5 | @books = create :resource, name: 'Free books' 6 | @videos = create :resource, name: 'Free videos' 7 | 8 | @books.tag_list.add('books') 9 | @books.save 10 | end 11 | 12 | test '.with_tags returns all resources when no arguments are passed' do 13 | assert_equal Resource.with_tags, [@books, @videos] 14 | end 15 | 16 | test '.with_tags returns only relevant tagged resources when tagged arguments are passed' do 17 | assert_equal Resource.with_tags('books'), [@books] 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/models/role_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RoleTest < ActiveSupport::TestCase 4 | test 'role requires a title' do 5 | role = build(:role, title: '') 6 | 7 | refute role.valid? 8 | end 9 | 10 | test 'title must be unique' do 11 | standard_role = create :role, title: 'standard' 12 | assert standard_role.valid? 13 | 14 | duplicate_role = build :role, title: 'standard' 15 | duplicate_role.save 16 | 17 | refute duplicate_role.valid? 18 | assert duplicate_role.errors.full_messages.include? 'Title has already been taken' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/models/scholarship_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ScholarshipTest < ActiveSupport::TestCase 4 | test 'unexpired returns unexpired scholarships' do 5 | nil_date = create :scholarship, close_time: nil 6 | active_date = create :scholarship, close_time: 3.days.from_now 7 | 8 | assert_equal Scholarship.unexpired, [nil_date, active_date] 9 | end 10 | 11 | test 'unexpired doesn\'t return expired scholarships' do 12 | expired_date = create :scholarship, close_time: 3.days.ago 13 | 14 | assert_not_includes Scholarship.unexpired, expired_date 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/models/team_member_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TeamMemberTest < ActiveSupport::TestCase 4 | test 'member must have name, role and group' do 5 | team_member = build(:team_member) 6 | assert team_member.valid? 7 | end 8 | 9 | test "member group must be 'team' or 'board'" do 10 | intern = build(:team_member, group: 'intern') 11 | team = build(:team_member, :team) 12 | board = build(:team_member, :board) 13 | executive = build(:team_member, :executive) 14 | 15 | assert intern.invalid? 16 | assert team.valid? 17 | assert board.valid? 18 | assert executive.valid? 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/requests/airtable/client_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ClientTest < Minitest::Test 4 | def test_get_records_for_gets_all_the_records_for_the_passed_table 5 | VCR.use_cassette('airtable/client/get_records_successful') do 6 | response = Airtable::Client.new.get_records_for('Services') 7 | 8 | assert response['records'].present? 9 | end 10 | end 11 | 12 | def test_post_record_creates_a_new_record_in_the_passed_table 13 | request_body = { 14 | 'fields' => { 15 | 'Slack User' => 'test_case_1', 16 | 'Service' => [ 17 | 'rec891lUSSaXM4qGC' 18 | ], 19 | 'Email' => 'test@example.com', 20 | 'Skillsets' => [ 21 | 'Java' 22 | ], 23 | 'Additional Details' => 'Some test description.', 24 | 'Mentor Requested' => [ 25 | 'recqeVhDDJU5cY8TX' 26 | ] 27 | } 28 | }.to_json 29 | 30 | VCR.use_cassette('airtable/client/post_record_successful') do 31 | response = Airtable::Client.new.post_record('Mentor Request', request_body) 32 | 33 | assert response['id'].present? 34 | assert response['createdTime'].present? 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/requests/git_hub/issues_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class IssuesTest < Minitest::Test 4 | def setup 5 | GitHubStatistic.delete_all 6 | GitHubUser.delete_all 7 | @issue_instance = GitHub::Issues.new 8 | @backend = GitHub::Settings::BACKEND 9 | client = GitHub::Client.new 10 | query = { 11 | repo: @backend, 12 | type: 'issue', 13 | is: 'closed', 14 | page_number: 1, 15 | filters: '+closed:>=1970-01-01' 16 | } 17 | 18 | @response = VCR.use_cassette('git_hub/search/issues/successful') do 19 | client.search_for(query) 20 | end 21 | 22 | stub_client_calls 23 | end 24 | 25 | def test_build_details_for 26 | @issue_instance.send(:build_details_for, @backend) 27 | 28 | issues_assertions 29 | end 30 | 31 | def test_get_issues 32 | @issue_instance.send(:get_issues) 33 | 34 | issues_assertions 35 | end 36 | 37 | def test_fetch_and_save 38 | @issue_instance.fetch_and_save!(print_results: false) 39 | 40 | assert_equal 28, GitHubStatistic.for_repository(@backend).count 41 | assert_equal 28, GitHubStatistic.for_repository(@backend).issues.count 42 | assert_equal 28, GitHubStatistic.for_repository(@backend).pluck(:title).uniq.count 43 | assert_equal 28, GitHubStatistic.for_repository(@backend).pluck(:number).uniq.count 44 | assert_equal 4, GitHubUser.count 45 | end 46 | 47 | def stub_client_calls 48 | @issue_instance.client.stubs(:repositories).returns([@backend]) 49 | @issue_instance.stubs(:get_all_issues_for).with(@backend).returns(@response['items']) 50 | end 51 | 52 | def issues_assertions 53 | assert_equal 28, @issue_instance.compiled_issues.size 54 | assert_equal ['Issue'], @issue_instance.compiled_issues.map { |stat| stat[:source_type] }.uniq 55 | assert_equal ['closed'], @issue_instance.compiled_issues.map { |stat| stat[:state] }.uniq 56 | assert_equal ['operationcode_backend'], @issue_instance.compiled_issues.map { |stat| stat[:repository] }.uniq 57 | assert_equal 'https://github.com/OperationCode/operationcode_backend/issues/133', @issue_instance.compiled_issues.first[:url] 58 | assert_equal 'Support for multi step profiles', @issue_instance.compiled_issues.first[:title] 59 | assert_equal 133, @issue_instance.compiled_issues.first[:number] 60 | assert_equal 248273181, @issue_instance.compiled_issues.first[:source_id] 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /test/requests/git_hub/page_compiler_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GitHub::PageCompilerTest < Minitest::Test 4 | def setup 5 | @client = GitHub::Client.new 6 | end 7 | 8 | def test_for_compile_prs 9 | query = { 10 | repo: GitHub::Settings::OC, 11 | type: 'pr', 12 | is: 'merged', 13 | page_number: 1, 14 | filters: '+merged:>=1970-01-01' 15 | } 16 | 17 | response = VCR.use_cassette('git_hub/search/pull_requests/success_for_multi_page') do 18 | @client.search_for(query) 19 | end 20 | 21 | compiled_prs = VCR.use_cassette('git_hub/search/pull_requests/compile_prs') do 22 | GitHub::PageCompiler.new(query, response, @client).compile_prs 23 | end 24 | 25 | pr = compiled_prs.last 26 | 27 | assert_equal 249, compiled_prs.size 28 | assert_equal Array, compiled_prs.class 29 | assert_equal Hash, pr.class 30 | assert_equal 'https://github.com/OperationCode/operationcode/pull/2', pr['html_url'] 31 | assert_equal 66372782, pr['id'] 32 | assert_equal 2, pr['number'] 33 | assert_equal 'Ignore OSX system files', pr['title'] 34 | assert_equal 'closed', pr['state'] 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/requests/slack_service/client_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ClientTest < Minitest::Test 4 | def test_invite_success 5 | VCR.use_cassette('slack_service/client/invite_success') do 6 | response = SlackService::Client.new.invite('test@example.com') 7 | assert response['ok'].present? 8 | json = JSON.parse(response) 9 | assert_equal true, json['ok'] 10 | end 11 | end 12 | 13 | def test_invite_failure 14 | VCR.use_cassette('slack_service/client/invite_failure') do 15 | assert_raises { SlackService::Client.new.invite('test@example.com') } 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/support/code_schools/test_config.yml: -------------------------------------------------------------------------------- 1 | - name: Test Name 2 | url: https://www.test-name.example.com 3 | full_time: true 4 | hardware_included: false 5 | notes: Test Note 6 | locations: 7 | - va_accepted: false 8 | address1: Test Address 1 9 | address2: Test Address 2 10 | city: Testcity 11 | state: TS 12 | zip: 11772 13 | - va_accepted: true 14 | address1: Test Address 3 15 | address2: Test Address 4 16 | city: Testcity 17 | state: TS 18 | zip: 11772 19 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | require 'mocha/mini_test' 5 | require 'factory_girl' 6 | require 'sidekiq/testing' 7 | require 'vcr' 8 | 9 | class ActiveSupport::TestCase 10 | include FactoryGirl::Syntax::Methods 11 | include Rails.application.routes.url_helpers 12 | 13 | def authorization_headers(user) 14 | { 'Authorization': "bearer #{user.token}" } 15 | end 16 | 17 | # @see https://gist.github.com/mattbrictson/72910465f36be8319cde 18 | # 19 | # Monkey patch the `test` DSL to enable VCR and configure a cassette named 20 | # based on the test method. This means that a test written like this: 21 | # 22 | # class OrderTest < ActiveSupport::TestCase 23 | # test "user can place order" do 24 | # ... 25 | # end 26 | # end 27 | # 28 | # will automatically use VCR to intercept and record/play back any external 29 | # HTTP requests using `cassettes/order_test/_user_can_place_order.yml`. 30 | # 31 | def self.test(test_name, &block) 32 | return super if block.nil? 33 | 34 | cassette = [name, test_name].map do |str| 35 | str.underscore.gsub(/[^A-Z]+/i, '_') 36 | end.join('/') 37 | 38 | super(test_name) do 39 | VCR.use_cassette(cassette) do 40 | instance_eval(&block) 41 | end 42 | end 43 | end 44 | end 45 | 46 | VCR.configure do |config| 47 | config.allow_http_connections_when_no_cassette = false 48 | config.cassette_library_dir = 'test/cassettes' 49 | config.hook_into :webmock 50 | end 51 | 52 | Geocoder.configure(lookup: :test) 53 | Geocoder::Lookup::Test.set_default_stub( 54 | [{ 'latitude' => -4, 55 | 'longitude' => -4, 56 | 'address' => 'a weird default, when real world accuracy does not matter', 57 | 'state_code' => 'DEFAULT' }] 58 | ) 59 | Geocoder::Lookup::Test.add_stub( 60 | '97201', [{ 'latitude' => 45.505603, 61 | 'longitude' => -122.6882145, 62 | 'address' => 'real world result for the zip used in the user factory', 63 | 'state_code' => 'OR' }] 64 | ) 65 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/tmp/.keep --------------------------------------------------------------------------------