├── .circleci ├── config.yml └── setup-heroku.sh ├── .credo.exs ├── .editorconfig ├── .env.example ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE.md ├── Procfile ├── README.md ├── USAGE.md ├── bin └── deploy_docs.sh ├── blueprint └── api.apib ├── civic.json ├── config ├── config.exs ├── dev.exs ├── prod.exs ├── remote-development.exs ├── scout_apm.exs ├── staging.exs ├── test.exs └── timber.exs ├── dialyzer.ignore-warnings ├── docs ├── API.md ├── INSTALLING.md └── SQUASHING.md ├── elixir_buildpack.config ├── emails ├── forgot_password.html ├── message_initiated_by_project.html ├── organization_invite.html ├── project_approval_request.html ├── project_approved.html ├── project_user_acceptance.html ├── project_user_request.html ├── receipt.html ├── reply_to_conversation.html └── styles.css ├── lib ├── code_corps.ex ├── code_corps │ ├── accounts │ │ ├── accounts.ex │ │ ├── changesets.ex │ │ └── users.ex │ ├── adapter │ │ └── map_transformer.ex │ ├── admin │ │ └── github_event_query.ex │ ├── analytics │ │ ├── in_memory_api.ex │ │ ├── segment_api.ex │ │ ├── segment_data_extractor.ex │ │ ├── segment_event_name_builder.ex │ │ ├── segment_plug_tracker.ex │ │ ├── segment_tracker.ex │ │ ├── segment_tracking_support.ex │ │ ├── segment_traits_builder.ex │ │ └── test_api.ex │ ├── auth │ │ ├── bearer_auth_pipeline.ex │ │ ├── ensure_auth_pipeline.ex │ │ └── error_handler.ex │ ├── cloudex │ │ ├── cloudex_test.ex │ │ ├── cloudinary_url.ex │ │ └── uploader.ex │ ├── comment │ │ └── service.ex │ ├── conn_utils.ex │ ├── emails │ │ ├── base_email.ex │ │ ├── forgot_password_email.ex │ │ ├── message_initiated_by_project_email.ex │ │ ├── organization_invite_email.ex │ │ ├── project_approval_request_email.ex │ │ ├── project_approved_email.ex │ │ ├── project_user_acceptance_email.ex │ │ ├── project_user_request_email.ex │ │ ├── receipt_email.ex │ │ └── reply_to_conversation_email.ex │ ├── github │ │ ├── adapters │ │ │ ├── app_installation.ex │ │ │ ├── comment.ex │ │ │ ├── issue.ex │ │ │ ├── pull_request.ex │ │ │ ├── repo.ex │ │ │ ├── user.ex │ │ │ └── utils │ │ │ │ └── body_decorator.ex │ │ ├── api │ │ │ ├── api.ex │ │ │ ├── comment.ex │ │ │ ├── errors │ │ │ │ └── pagination_error.ex │ │ │ ├── gateway.ex │ │ │ ├── headers.ex │ │ │ ├── installation.ex │ │ │ ├── issue.ex │ │ │ ├── jwt.ex │ │ │ ├── pagination.ex │ │ │ ├── pull_request.ex │ │ │ ├── repository.ex │ │ │ └── user.ex │ │ ├── event.ex │ │ ├── event │ │ │ ├── handler.ex │ │ │ ├── installation │ │ │ │ ├── installation.ex │ │ │ │ └── validator.ex │ │ │ ├── installation_repositories │ │ │ │ ├── installation_repositories.ex │ │ │ │ └── validator.ex │ │ │ ├── issue_comment │ │ │ │ ├── issue_comment.ex │ │ │ │ └── validator.ex │ │ │ ├── issues │ │ │ │ ├── issues.ex │ │ │ │ └── validator.ex │ │ │ ├── pull_request │ │ │ │ ├── pull_request.ex │ │ │ │ └── validator.ex │ │ │ └── validator.ex │ │ ├── github.ex │ │ ├── sync │ │ │ ├── comment │ │ │ │ ├── changeset.ex │ │ │ │ └── comment.ex │ │ │ ├── github_app_installation │ │ │ │ ├── changeset.ex │ │ │ │ └── github_app_installation.ex │ │ │ ├── github_comment │ │ │ │ └── github_comment.ex │ │ │ ├── github_issue │ │ │ │ └── github_issue.ex │ │ │ ├── github_pull_request │ │ │ │ ├── body_parser.ex │ │ │ │ └── gitub_pull_request.ex │ │ │ ├── github_repo │ │ │ │ └── github_repo.ex │ │ │ ├── github_user │ │ │ │ ├── changeset.ex │ │ │ │ └── github_user.ex │ │ │ ├── sync.ex │ │ │ ├── task │ │ │ │ ├── changeset.ex │ │ │ │ └── task.ex │ │ │ ├── user │ │ │ │ ├── record_linker.ex │ │ │ │ └── user.ex │ │ │ └── utils │ │ │ │ └── finder.ex │ │ ├── utils │ │ │ └── result_aggregator.ex │ │ └── webhook │ │ │ ├── event_support.ex │ │ │ └── handler.ex │ ├── guardian.ex │ ├── helpers │ │ ├── query.ex │ │ ├── random_icon_color.ex │ │ ├── slug.ex │ │ ├── string.ex │ │ └── url.ex │ ├── mailer.ex │ ├── map_utils.ex │ ├── messages │ │ ├── conversation_parts.ex │ │ ├── conversation_query.ex │ │ ├── conversations.ex │ │ ├── emails.ex │ │ └── messages.ex │ ├── model │ │ ├── auth_token.ex │ │ ├── category.ex │ │ ├── comment.ex │ │ ├── conversation.ex │ │ ├── conversation_part.ex │ │ ├── donation_goal.ex │ │ ├── github_app_installation.ex │ │ ├── github_comment.ex │ │ ├── github_event.ex │ │ ├── github_issue.ex │ │ ├── github_issue_assignee.ex │ │ ├── github_pull_request.ex │ │ ├── github_repo.ex │ │ ├── github_user.ex │ │ ├── message.ex │ │ ├── model.ex │ │ ├── organization.ex │ │ ├── organization_github_app_installation.ex │ │ ├── organization_invite.ex │ │ ├── preview.ex │ │ ├── project.ex │ │ ├── project_category.ex │ │ ├── project_skill.ex │ │ ├── project_user.ex │ │ ├── role.ex │ │ ├── role_skill.ex │ │ ├── skill.ex │ │ ├── slugged_route.ex │ │ ├── stripe_connect_account.ex │ │ ├── stripe_connect_card.ex │ │ ├── stripe_connect_charge.ex │ │ ├── stripe_connect_customer.ex │ │ ├── stripe_connect_plan.ex │ │ ├── stripe_connect_subscription.ex │ │ ├── stripe_event.ex │ │ ├── stripe_external_account.ex │ │ ├── stripe_file_upload.ex │ │ ├── stripe_invoice.ex │ │ ├── stripe_platform_card.ex │ │ ├── stripe_platform_customer.ex │ │ ├── task.ex │ │ ├── task_list.ex │ │ ├── task_skill.ex │ │ ├── user.ex │ │ ├── user_category.ex │ │ ├── user_role.ex │ │ ├── user_skill.ex │ │ └── user_task.ex │ ├── organizations │ │ └── organizations.ex │ ├── policy │ │ ├── category.ex │ │ ├── comment.ex │ │ ├── conversation.ex │ │ ├── conversation_part.ex │ │ ├── donation_goal.ex │ │ ├── github_app_installation.ex │ │ ├── github_event.ex │ │ ├── github_repo.ex │ │ ├── helpers.ex │ │ ├── message.ex │ │ ├── organization.ex │ │ ├── organization_github_app_installation.ex │ │ ├── organization_invite.ex │ │ ├── policy.ex │ │ ├── preview.ex │ │ ├── project.ex │ │ ├── project_category.ex │ │ ├── project_skill.ex │ │ ├── project_user.ex │ │ ├── role.ex │ │ ├── role_skill.ex │ │ ├── skill.ex │ │ ├── stripe_connect_account.ex │ │ ├── stripe_connect_plan.ex │ │ ├── stripe_connect_subscription.ex │ │ ├── stripe_platform_card.ex │ │ ├── stripe_platform_customer.ex │ │ ├── task.ex │ │ ├── task_skill.ex │ │ ├── user.ex │ │ ├── user_category.ex │ │ ├── user_role.ex │ │ ├── user_skill.ex │ │ └── user_task.ex │ ├── presenters │ │ └── image_presenter.ex │ ├── processor │ │ ├── async.ex │ │ ├── processor.ex │ │ └── sync.ex │ ├── project │ │ └── query.ex │ ├── projects │ │ └── projects.ex │ ├── random_icon_color │ │ ├── generator.ex │ │ └── test_generator.ex │ ├── repo.ex │ ├── sentry │ │ ├── async.ex │ │ ├── sentry.ex │ │ └── sync.ex │ ├── services │ │ ├── donation_goals_service.ex │ │ ├── forgot_password.ex │ │ ├── markdown_renderer_service.ex │ │ ├── project_service.ex │ │ └── user_service.ex │ ├── skills │ │ └── skills.ex │ ├── stripe_service │ │ ├── adapters │ │ │ ├── stripe_connect_account.ex │ │ │ ├── stripe_connect_card.ex │ │ │ ├── stripe_connect_charge.ex │ │ │ ├── stripe_connect_customer.ex │ │ │ ├── stripe_connect_plan.ex │ │ │ ├── stripe_connect_subscription.ex │ │ │ ├── stripe_event.ex │ │ │ ├── stripe_external_account.ex │ │ │ ├── stripe_file_upload.ex │ │ │ ├── stripe_invoice.ex │ │ │ ├── stripe_platform_card.ex │ │ │ └── stripe_platform_customer.ex │ │ ├── events │ │ │ ├── account_updated.ex │ │ │ ├── connect_charge_succeeded.ex │ │ │ ├── connect_external_account_created.ex │ │ │ ├── customer_source_updated.ex │ │ │ ├── customer_subscription_deleted.ex │ │ │ ├── customer_subscription_updated.ex │ │ │ ├── customer_updated.ex │ │ │ └── invoice_payment_succeeded.ex │ │ ├── stripe_connect_account_service.ex │ │ ├── stripe_connect_card.ex │ │ ├── stripe_connect_charge_service.ex │ │ ├── stripe_connect_customer.ex │ │ ├── stripe_connect_external_account_service.ex │ │ ├── stripe_connect_plan.ex │ │ ├── stripe_connect_subscription_service.ex │ │ ├── stripe_invoice_service.ex │ │ ├── stripe_platform_card.ex │ │ ├── stripe_platform_customer.ex │ │ ├── validators │ │ │ ├── project_can_enable_donations.ex │ │ │ ├── project_subscribable.ex │ │ │ └── user_can_subscribe.ex │ │ └── webhook_processing │ │ │ ├── connect_event_handler.ex │ │ │ ├── environment_filter.ex │ │ │ ├── event_handler.ex │ │ │ ├── ignored_event_handler.ex │ │ │ ├── platform_event_handler.ex │ │ │ └── webhook_processor.ex │ ├── stripe_testing │ │ ├── account.ex │ │ ├── card.ex │ │ ├── charge.ex │ │ ├── customer.ex │ │ ├── event.ex │ │ ├── external_account.ex │ │ ├── fixtures │ │ │ ├── account.json │ │ │ ├── account_with_multiple_external_accounts.json │ │ │ ├── charge.json │ │ │ └── invoice.json │ │ ├── helpers.ex │ │ ├── invoice.ex │ │ ├── plan.ex │ │ ├── subscription.ex │ │ └── token.ex │ ├── tasks │ │ ├── query.ex │ │ └── tasks.ex │ ├── transition │ │ └── user_state.ex │ ├── validators │ │ ├── slug_validator.ex │ │ └── time_validator.ex │ └── web_client.ex ├── code_corps_web.ex └── code_corps_web │ ├── channels │ ├── conversation_channel.ex │ └── user_socket.ex │ ├── controllers │ ├── category_controller.ex │ ├── comment_controller.ex │ ├── conversation_controller.ex │ ├── conversation_part_controller.ex │ ├── donation_goal_controller.ex │ ├── fallback_controller.ex │ ├── github_app_installation_controller.ex │ ├── github_event_controller.ex │ ├── github_issue_controller.ex │ ├── github_pull_request_controller.ex │ ├── github_repo_controller.ex │ ├── message_controller.ex │ ├── organization_controller.ex │ ├── organization_github_app_installation_controller.ex │ ├── organization_invite_controller.ex │ ├── page_controller.ex │ ├── password_controller.ex │ ├── password_reset_controller.ex │ ├── preview_controller.ex │ ├── project_category_controller.ex │ ├── project_controller.ex │ ├── project_skill_controller.ex │ ├── project_user_controller.ex │ ├── role_controller.ex │ ├── role_skill_controller.ex │ ├── skill_controller.ex │ ├── slugged_route_controller.ex │ ├── stripe_connect_account_controller.ex │ ├── stripe_connect_events_controller.ex │ ├── stripe_connect_plan_controller.ex │ ├── stripe_connect_subscription_controller.ex │ ├── stripe_platform_card_controller.ex │ ├── stripe_platform_customer_controller.ex │ ├── stripe_platform_events_controller.ex │ ├── task_controller.ex │ ├── task_list_controller.ex │ ├── task_skill_controller.ex │ ├── token_controller.ex │ ├── user_category_controller.ex │ ├── user_controller.ex │ ├── user_role_controller.ex │ ├── user_skill_controller.ex │ └── user_task_controller.ex │ ├── endpoint.ex │ ├── gettext.ex │ ├── plugs │ ├── analytics_identify.ex │ ├── current_user.ex │ ├── data_to_attributes.ex │ ├── ids_to_integers.ex │ ├── segment.ex │ ├── set_sentry_user_context.ex │ └── set_timber_user_context.ex │ ├── router.ex │ └── views │ ├── category_view.ex │ ├── changeset_view.ex │ ├── comment_view.ex │ ├── conversation_part_view.ex │ ├── conversation_view.ex │ ├── donation_goal_view.ex │ ├── error_helpers.ex │ ├── error_view.ex │ ├── github_app_installation_view.ex │ ├── github_event_view.ex │ ├── github_issue_view.ex │ ├── github_pull_request_view.ex │ ├── github_repo_view.ex │ ├── layout_view.ex │ ├── message_view.ex │ ├── organization_github_app_installation_view.ex │ ├── organization_invite_view.ex │ ├── organization_view.ex │ ├── page_view.ex │ ├── password_reset_view.ex │ ├── password_view.ex │ ├── preview_view.ex │ ├── project_category_view.ex │ ├── project_skill_view.ex │ ├── project_user_view.ex │ ├── project_view.ex │ ├── role_skill_view.ex │ ├── role_view.ex │ ├── skill_view.ex │ ├── slugged_route_view.ex │ ├── stripe_connect_account_view.ex │ ├── stripe_connect_plan_view.ex │ ├── stripe_connect_subscription_view.ex │ ├── stripe_platform_card_view.ex │ ├── stripe_platform_customer_view.ex │ ├── task_list_view.ex │ ├── task_skill_view.ex │ ├── task_view.ex │ ├── token_view.ex │ ├── user_category_view.ex │ ├── user_role_view.ex │ ├── user_skill_view.ex │ ├── user_task_view.ex │ └── user_view.ex ├── mix.exs ├── mix.lock ├── priv ├── gettext │ ├── default.pot │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── default.po │ │ │ └── errors.po │ └── errors.pot ├── repo │ ├── add_default_tasks_to_projects.exs │ ├── migrations │ │ ├── 20160723215749_create_user.exs │ │ ├── 20160804000000_create_organization.exs │ │ ├── 20160804001111_create_slugged_route.exs │ │ ├── 20160805132301_add_user_profile_fields.exs │ │ ├── 20160805203929_create_skill.exs │ │ ├── 20160808143454_create_category.exs │ │ ├── 20160809214736_create_role.exs │ │ ├── 20160810124357_create_project.exs │ │ ├── 20160815125009_create_preview.exs │ │ ├── 20160815143002_create_user_skill.exs │ │ ├── 20160816020347_create_post.exs │ │ ├── 20160816034021_create_role_skill.exs │ │ ├── 20160817220118_create_comment.exs │ │ ├── 20160818000944_add_post_number_incrementing.exs │ │ ├── 20160818132546_add_organization_membership.exs │ │ ├── 20160820113856_add_project_category.exs │ │ ├── 20160820164905_add_photo_to_users.exs │ │ ├── 20160822002438_add_icon_to_projects.exs │ │ ├── 20160822004056_add_icon_to_organizations.exs │ │ ├── 20160822011624_create_user_role.exs │ │ ├── 20160822020401_create_user_category.exs │ │ ├── 20160822044612_create_project_skill.exs │ │ ├── 20160830081224_add_admin_to_user.exs │ │ ├── 20160830224802_add_state_to_users.exs │ │ ├── 20160911233738_remove_icon_urls_from_projects.exs │ │ ├── 20160912002705_change_string_to_text_where_needed.exs │ │ ├── 20160912145957_add_cat_to_role_skill.exs │ │ ├── 20160918003206_fix_mixed_case_slugs.exs │ │ ├── 20160928232404_change_posts_to_tasks.exs │ │ ├── 20161003185918_add_not_null_constraints.exs │ │ ├── 20161019090945_add_stripe_customers_cards_tables.exs │ │ ├── 20161019110737_create_donation_goal.exs │ │ ├── 20161020144622_create_stripe_account.exs │ │ ├── 20161021131026_create_stripe_plans_stripe_subscriptions.exs │ │ ├── 20161031001615_remove_title_from_donation_goals.exs │ │ ├── 20161121005339_rename_stripe_customers.exs │ │ ├── 20161121014050_create_stripe_connect_customers.exs │ │ ├── 20161121043941_rename_stripe_platform_cards.exs │ │ ├── 20161121045709_create_stripe_connect_cards.exs │ │ ├── 20161122015942_rename_stripe_connect_accounts.exs │ │ ├── 20161123081114_rename_stripe_connect_plans.exs │ │ ├── 20161123150943_rename_stripe_connect_subscriptions.exs │ │ ├── 20161124085742_switch_from_multiple_to_single_card.exs │ │ ├── 20161125200620_add_total_donated_to_project.exs │ │ ├── 20161126045705_add_current_donation_goal.exs │ │ ├── 20161127054559_revert_back_to_current_on_donation_goals.exs │ │ ├── 20161205024856_add_approved_to_projects.exs │ │ ├── 20161207112519_add_stripe_events.exs │ │ ├── 20161209192504_create_task_list.exs │ │ ├── 20161212005641_create_stripe_file_upload.exs │ │ ├── 20161214005935_create_stripe_external_account.exs │ │ ├── 20161215052051_add_user_id_and_endpoint_to_stripe_events.exs │ │ ├── 20161216051447_add_approved_to_organizations.exs │ │ ├── 20161218005913_add_verification_fields_to_stripe_connect_account.exs │ │ ├── 20161219160401_change_external_account_default_for_currency_to_boolean.exs │ │ ├── 20161219163909_add_external_account_to_stripe_connect_accounts.exs │ │ ├── 20161220141753_add_status_fields_to_stripe_connect_accounts.exs │ │ ├── 20161221085759_add_legal_entity_fields_to_stripe_connect_account.exs │ │ ├── 20161226213600_remove_obsolete_status_fields_from_connect_account.exs │ │ ├── 20161231063614_alter_dob_fields_to_integer.exs │ │ ├── 20170102130055_add_tos_acceptance_fields_to_stripe_connect_accounts.exs │ │ ├── 20170102181053_convert_stripe_time_stamps_to_integers.exs │ │ ├── 20170104113708_add_stripe_connect_account_reference_to_external_accounts.exs │ │ ├── 20170104212623_add_invoices.exs │ │ ├── 20170104235423_add_user_to_stripe_connect_customer.exs │ │ ├── 20170106013143_add_editable_to_task_lists.exs │ │ ├── 20170115035159_add_object_id_type_to_events.exs │ │ ├── 20170115230549_add_ignored_reason_to_stripe_events.exs │ │ ├── 20170121014100_add_stripe_connect_charge.exs │ │ ├── 20170131234029_create_task_skill.exs │ │ ├── 20170201014901_add_cloudinary_public_ids.exs │ │ ├── 20170201025454_add_default_colors.exs │ │ ├── 20170201035458_create_user_task.exs │ │ ├── 20170201183258_remove_photo_icon_columns.exs │ │ ├── 20170220032224_remove_task_type_from_tasks.exs │ │ ├── 20170224233516_add_owner_to_organization.exs │ │ ├── 20170226050552_add_owner_to_projects.exs │ │ ├── 20170228085250_create_project_users.exs │ │ ├── 20170308214128_add_website_to_project.exs │ │ ├── 20170308220713_add_should_link_externally_to_project.exs │ │ ├── 20170308222552_create_auth_token.exs │ │ ├── 20170313130611_remove_project_owner_id.exs │ │ ├── 20170318032449_add_check_constraint_to_project_description_when_project_is_approved.exs │ │ ├── 20170318082740_drop_organization_membership.exs │ │ ├── 20170324194827_add_sign_up_context_to_users.exs │ │ ├── 20170424215355_add-github-id-to-user.exs │ │ ├── 20170501225441_add_github_ids.exs │ │ ├── 20170505224222_add_github_repo_and_owner_to_project.exs │ │ ├── 20170526095401_add_github_auth_token_to_users.exs │ │ ├── 20170602000208_add_github_details_to_users.exs │ │ ├── 20170622205732_create_github_app_installation.exs │ │ ├── 20170626231059_create_github_event.exs │ │ ├── 20170628092119_add_github_repos.exs │ │ ├── 20170628213609_add_access_tokens_to_github_app_installations.exs │ │ ├── 20170629183404_add_unique_index_on_github_id_to_github_app_installations.exs │ │ ├── 20170630140136_create_project_github_repo.exs │ │ ├── 20170706132431_add_github_app_installation_sender_github_id.exs │ │ ├── 20170707213648_create_organization_github_app_installation.exs │ │ ├── 20170711122252_add_github_app_installation_origin_field.exs │ │ ├── 20170717092127_allow_user_email_username_null.exs │ │ ├── 20170725060612_add_github_account_fields_to_github_app_installation.exs │ │ ├── 20170727052644_create_organization_invite.exs │ │ ├── 20170731130121_remove_task_state.exs │ │ ├── 20170814131722_link_task_to_github.exs │ │ ├── 20170913114958_remove_github_event_source_field.exs │ │ ├── 20170921014405_loosen_markdown_restrictions.exs │ │ ├── 20170925214512_add_type_to_users.exs │ │ ├── 20170925230419_add_payload_to_git_hub_events.exs │ │ ├── 20170926134646_add_failure_reason_to_github_events.exs │ │ ├── 20170927100300_add_closed_at_to_tasks.exs │ │ ├── 20170928234412_add_created_at_and_modified_at_to_tasks.exs │ │ ├── 20171003134956_add_unique_constraint_to_users_github_id.exs │ │ ├── 20171003225853_add_created_at_and_modified_at_to_comments.exs │ │ ├── 20171006063358_add_archived_to_tasks.exs │ │ ├── 20171006161407_change_organization_invite_title_to_organization_name.exs │ │ ├── 20171012215106_create_github_issues.exs │ │ ├── 20171012221231_change_github_issue_relationships.exs │ │ ├── 20171016125229_add_github_comment.exs │ │ ├── 20171016125516_change_github_comment_relationships.exs │ │ ├── 20171016223356_create_github_pull_requests.exs │ │ ├── 20171016235656_add_pull_requests_to_tasks.exs │ │ ├── 20171017235433_add_missing_github_id_indexes.exs │ │ ├── 20171019191035_change_github_issue_and_github_pull_request_relationships.exs │ │ ├── 20171025184225_add_missing_indexes.exs │ │ ├── 20171026010933_add_more_missing_indexes.exs │ │ ├── 20171027061833_add_more_indexes_again.exs │ │ ├── 20171028011642_update_null_task_values_from_github_integration.exs │ │ ├── 20171028173508_update_null_comment_values_from_github_integrations.exs │ │ ├── 20171030182857_add_sync_statuses_to_github_repo.exs │ │ ├── 20171031232023_add_sync_state_to_project_github_repos.exs │ │ ├── 20171031234356_create_github_users.exs │ │ ├── 20171101023309_add_github_repo_to_github_comments.exs │ │ ├── 20171104013543_add_indexes_for_syncing.exs │ │ ├── 20171106045740_add_done_to_task_list.exs │ │ ├── 20171106050209_add_pull_requests_to_task_list.exs │ │ ├── 20171106103153_add_unique_constraints_to_specific_task_lists.exs │ │ ├── 20171106200036_archive_outdated_tasks.exs │ │ ├── 20171109231538_add_github_id_was.exs │ │ ├── 20171110001134_add_index_for_github_id_was_to_users.exs │ │ ├── 20171114010851_migrate_unsupported_github_events.exs │ │ ├── 20171114033357_add_unique_constraint_for_project_github_repo_and_project.exs │ │ ├── 20171114225214_add_project_id_to_github_repo.exs │ │ ├── 20171114225713_migrate_project_github_repos_to_github_repo.exs │ │ ├── 20171114232534_remove_project_github_repos.exs │ │ ├── 20171115201624_drop_github_repos_project_id_unique_index_if_exists.exs │ │ ├── 20171115225358_add_serialized_error_to_github_events.exs │ │ ├── 20171119004204_create_github_issue_assignees.exs │ │ ├── 20171121075226_migrate_stripe_connect_accounts.exs │ │ ├── 20171121144138_change_managed_to_type_on_stripe_connect_account.exs │ │ ├── 20171123065902_remove_github_repo_and_owner_from_project.exs │ │ ├── 20171127215847_change_organization_invite_fulfillment.exs │ │ ├── 20171201073818_add_approval_requested_to_projects.exs │ │ ├── 20171205161052_create_messages.exs │ │ ├── 20171213062707_add_conversation_models.exs │ │ ├── 20171220154922_add_part_type_to_conversation.exs │ │ └── 20180113002017_normalize_organization_user_type.exs │ ├── seeds.exs │ └── structure.sql └── static │ └── robots.txt └── test ├── fixtures └── github │ ├── app.pem │ ├── endpoints │ ├── forbidden.json │ ├── installation_repositories.json │ ├── installations_access_tokens.json │ ├── issue.json │ ├── issue_comment.json │ ├── issues.json │ ├── issues_comments.json │ ├── pull_request.json │ ├── pulls.json │ └── user.json │ └── events │ ├── installation_created.json │ ├── installation_repositories_added.json │ ├── installation_repositories_removed.json │ ├── issue_comment_created.json │ ├── issue_comment_created_by_bot.json │ ├── issue_comment_created_on_pull_request.json │ ├── issue_comment_deleted.json │ ├── issue_comment_edited.json │ ├── issues_closed.json │ ├── issues_edited.json │ ├── issues_opened.json │ ├── issues_opened_by_bot.json │ ├── issues_reopened.json │ ├── pull_request_closed.json │ ├── pull_request_edited.json │ ├── pull_request_opened.json │ ├── pull_request_opened_by_bot.json │ ├── pull_request_reopened.json │ ├── pull_request_synchronize.json │ └── user_repositories.json ├── lib ├── code_corps │ ├── accounts │ │ ├── accounts_test.exs │ │ ├── changesets_test.exs │ │ └── users_test.exs │ ├── adapter │ │ └── map_transformer_test.exs │ ├── admin │ │ └── github_event_query_test.exs │ ├── analytics │ │ ├── segment_data_extractor_test.exs │ │ ├── segment_event_name_builder_test.exs │ │ └── segment_traits_builder_test.exs │ ├── cloudex │ │ ├── cloudinary_url_test.exs │ │ └── uploader_test.exs │ ├── comment │ │ └── service_test.exs │ ├── conn_utils_test.exs │ ├── emails │ │ ├── base_email_test.exs │ │ ├── forgot_password_email_test.exs │ │ ├── message_initiated_by_project_email_test.exs │ │ ├── organization_invite_email_test.exs │ │ ├── project_approval_request_email_test.exs │ │ ├── project_approved_email_test.exs │ │ ├── project_user_acceptance_email_test.exs │ │ ├── project_user_request_email_test.exs │ │ ├── receipt_email_test.exs │ │ └── reply_to_conversation_email_test.exs │ ├── github │ │ ├── adapters │ │ │ ├── app_installation_test.exs │ │ │ ├── comment_test.exs │ │ │ ├── issue_test.exs │ │ │ ├── pull_request_test.exs │ │ │ ├── repo_test.exs │ │ │ └── user_test.exs │ │ ├── api │ │ │ ├── api_test.exs │ │ │ ├── comment_test.exs │ │ │ ├── gateway_test.exs │ │ │ ├── headers_test.exs │ │ │ ├── installation_test.exs │ │ │ ├── issue_test.exs │ │ │ ├── pull_request_test.exs │ │ │ ├── repository_test.exs │ │ │ └── user_test.exs │ │ ├── event │ │ │ ├── installation │ │ │ │ ├── installation_test.exs │ │ │ │ └── validator_test.exs │ │ │ ├── installation_repositories │ │ │ │ ├── installation_repositories_test.exs │ │ │ │ └── validator_test.exs │ │ │ ├── issue_comment │ │ │ │ ├── issue_comment_test.exs │ │ │ │ └── validator_test.exs │ │ │ ├── issues │ │ │ │ ├── issues_test.exs │ │ │ │ └── validator_test.exs │ │ │ └── pull_request │ │ │ │ ├── pull_request_test.exs │ │ │ │ └── validator_test.exs │ │ ├── event_test.exs │ │ ├── github_test.exs │ │ ├── sync │ │ │ ├── comment │ │ │ │ ├── changeset_test.exs │ │ │ │ └── comment_test.exs │ │ │ ├── github_app_installation │ │ │ │ └── changeset_test.exs │ │ │ ├── github_comment │ │ │ │ └── github_comment_test.exs │ │ │ ├── github_issue │ │ │ │ └── github_issue_test.exs │ │ │ ├── github_pull_request │ │ │ │ ├── body_parser_test.exs │ │ │ │ └── github_pull_request_test.exs │ │ │ ├── github_repo │ │ │ │ └── github_repo_test.exs │ │ │ ├── github_user │ │ │ │ ├── changeset_test.exs │ │ │ │ └── github_user_test.exs │ │ │ ├── sync_test.exs │ │ │ ├── task │ │ │ │ ├── changeset_test.exs │ │ │ │ └── task_test.exs │ │ │ ├── user │ │ │ │ └── record_linker_test.exs │ │ │ └── utils │ │ │ │ └── finder_test.exs │ │ ├── utils │ │ │ └── result_aggregator_test.exs │ │ └── webhook │ │ │ ├── event_support_test.exs │ │ │ └── handler_test.exs │ ├── helpers │ │ ├── random_icon_color_test.exs │ │ └── url_test.exs │ ├── map_utils_test.exs │ ├── messages │ │ ├── conversation_parts_test.exs │ │ ├── conversation_query_test.exs │ │ ├── conversations_test.exs │ │ └── messages_test.exs │ ├── model │ │ ├── auth_token_test.exs │ │ ├── category_test.exs │ │ ├── comment_test.exs │ │ ├── donation_goal_test.exs │ │ ├── github_app_installation_test.exs │ │ ├── github_comment_test.exs │ │ ├── github_event_test.exs │ │ ├── github_issue_assignee_test.exs │ │ ├── github_issue_test.exs │ │ ├── github_pull_request_test.exs │ │ ├── github_repo_test.exs │ │ ├── message_test.exs │ │ ├── organization_github_app_installation_test.exs │ │ ├── organization_invite_test.exs │ │ ├── organization_test.exs │ │ ├── preview_test.exs │ │ ├── project_category_test.exs │ │ ├── project_skill_test.exs │ │ ├── project_test.exs │ │ ├── project_user_test.exs │ │ ├── role_skill_test.exs │ │ ├── role_test.exs │ │ ├── skill_test.exs │ │ ├── slugged_route_test.exs │ │ ├── stripe_connect_account_test.exs │ │ ├── stripe_connect_card_test.exs │ │ ├── stripe_connect_charge_test.exs │ │ ├── stripe_connect_customer_test.exs │ │ ├── stripe_connect_plan_test.exs │ │ ├── stripe_connect_subscription_test.exs │ │ ├── stripe_event_test.exs │ │ ├── stripe_external_account_test.exs │ │ ├── stripe_file_upload_test.exs │ │ ├── stripe_invoice_test.exs │ │ ├── stripe_platform_card_test.exs │ │ ├── stripe_platform_customer_test.exs │ │ ├── task_list_test.exs │ │ ├── task_skill_test.exs │ │ ├── task_test.exs │ │ ├── user_category_test.exs │ │ ├── user_role_test.exs │ │ ├── user_skill_test.exs │ │ ├── user_task_test.exs │ │ └── user_test.exs │ ├── organizations │ │ └── organizations_test.exs │ ├── policy │ │ ├── category_test.exs │ │ ├── comment_test.exs │ │ ├── conversation_part_test.exs │ │ ├── conversation_test.exs │ │ ├── donation_goal_test.exs │ │ ├── github_app_installation_test.exs │ │ ├── github_event_test.exs │ │ ├── github_repo_test.exs │ │ ├── helpers_test.exs │ │ ├── message_test.exs │ │ ├── organization_github_app_installation_test.exs │ │ ├── organization_invite_test.exs │ │ ├── organization_test.exs │ │ ├── preview_test.exs │ │ ├── project_category_test.exs │ │ ├── project_skill_test.exs │ │ ├── project_test.exs │ │ ├── project_user_test.exs │ │ ├── role_skill_test.exs │ │ ├── role_test.exs │ │ ├── skill_test.exs │ │ ├── stripe_connect_account_test.exs │ │ ├── stripe_connect_plan_test.exs │ │ ├── stripe_connect_subscription_test.exs │ │ ├── stripe_platform_card_test.exs │ │ ├── stripe_platform_customer_test.exs │ │ ├── task_skill_test.exs │ │ ├── task_test.exs │ │ ├── user_category_test.exs │ │ ├── user_role_test.exs │ │ ├── user_skill_test.exs │ │ ├── user_task_test.exs │ │ └── user_test.exs │ ├── presenters │ │ └── image_presenter_test.exs │ ├── random_icon_color │ │ ├── generator_test.exs │ │ └── test_generator_test.exs │ ├── services │ │ ├── donation_goals_test.exs │ │ ├── markdown_renderer_test.exs │ │ ├── project_test.exs │ │ └── user_service_test.exs │ ├── skills │ │ └── skills_test.exs │ ├── stripe_service │ │ ├── adapters │ │ │ ├── stripe_connect_account_test.exs │ │ │ ├── stripe_connect_charge_test.exs │ │ │ ├── stripe_connect_plan_test.exs │ │ │ ├── stripe_connect_subscription_test.exs │ │ │ ├── stripe_event_test.exs │ │ │ ├── stripe_external_account_test.exs │ │ │ ├── stripe_invoice_test.exs │ │ │ ├── stripe_platform_card_test.exs │ │ │ └── stripe_platform_customer_test.exs │ │ ├── events │ │ │ └── connect_charge_succeeded_test.exs │ │ ├── stripe_connect_account_service_test.exs │ │ ├── stripe_connect_card_service_test.exs │ │ ├── stripe_connect_charge_service_test.exs │ │ ├── stripe_connect_external_account_service_test.exs │ │ ├── stripe_connect_subscription_service_test.exs │ │ ├── stripe_invoice_service_test.exs │ │ ├── stripe_platform_card_service_test.exs │ │ ├── stripe_platform_customer_service_test.exs │ │ ├── validators │ │ │ └── project_can_enable_donations_test.exs │ │ └── webhook_processing │ │ │ ├── event_handler_test.exs │ │ │ └── ignored_event_handler_test.exs │ ├── tasks │ │ ├── query_test.exs │ │ └── tasks_test.exs │ ├── transition │ │ └── user_state_test.exs │ └── validators │ │ ├── slug_validator_test.exs │ │ └── time_validator_test.exs └── code_corps_web │ ├── channels │ └── conversation_channel_test.exs │ ├── controllers │ ├── category_controller_test.exs │ ├── comment_controller_test.exs │ ├── conversation_controller_test.exs │ ├── conversation_part_controller_test.exs │ ├── donation_goal_controller_test.exs │ ├── github_app_installation_controller_test.exs │ ├── github_event_controller_test.exs │ ├── github_issue_controller_test.exs │ ├── github_pull_request_controller_test.exs │ ├── github_repo_controller_test.exs │ ├── message_controller_test.exs │ ├── organization_controller_test.exs │ ├── organization_github_app_installation_controller_test.exs │ ├── organization_invite_controller_test.exs │ ├── page_controller_test.exs │ ├── password_controller_test.exs │ ├── password_reset_controller_test.exs │ ├── preview_controller_test.exs │ ├── project_category_controller_test.exs │ ├── project_controller_test.exs │ ├── project_skill_controller_test.exs │ ├── project_user_controller_test.exs │ ├── role_controller_test.exs │ ├── role_skill_controller_test.exs │ ├── skill_controller_test.exs │ ├── slugged_route_controller_test.exs │ ├── stripe_connect_account_controller_test.exs │ ├── stripe_connect_events_controller_test.exs │ ├── stripe_connect_plan_controller_test.exs │ ├── stripe_connect_subscription_controller_test.exs │ ├── stripe_platform_card_controller_test.exs │ ├── stripe_platform_customer_controller_test.exs │ ├── stripe_platform_events_controller_test.exs │ ├── task_controller_test.exs │ ├── task_list_controller_test.exs │ ├── task_skill_controller_test.exs │ ├── token_controller_test.exs │ ├── user_category_controller_test.exs │ ├── user_controller_test.exs │ ├── user_role_controller_test.exs │ ├── user_skill_controller_test.exs │ └── user_task_controller_test.exs │ ├── plugs │ ├── current_user_test.exs │ ├── data_to_attributes_test.exs │ └── set_timber_user_context_test.exs │ └── views │ ├── category_view_test.exs │ ├── changeset_view_test.exs │ ├── comment_view_test.exs │ ├── conversation_part_view_test.exs │ ├── conversation_view_test.exs │ ├── donation_goal_view_test.exs │ ├── error_view_test.exs │ ├── github_app_installation_view_test.exs │ ├── github_event_view_test.exs │ ├── github_issue_view_test.exs │ ├── github_pull_request_view_test.exs │ ├── github_repo_view_test.exs │ ├── layout_view_test.exs │ ├── message_view_test.exs │ ├── organization_github_app_installation_view_test.exs │ ├── organization_invite_view_test.exs │ ├── organization_view_test.exs │ ├── page_view_test.exs │ ├── password_reset_view_test.exs │ ├── password_view_test.exs │ ├── preview_view_test.exs │ ├── project_category_view_test.exs │ ├── project_skill_view_test.exs │ ├── project_user_view_test.exs │ ├── project_view_test.exs │ ├── role_skill_view_test.exs │ ├── role_view_test.exs │ ├── skill_view_test.exs │ ├── slugged_route_view_test.exs │ ├── stripe_connect_account_view_test.exs │ ├── stripe_connect_plan_view_test.exs │ ├── stripe_connect_subscription_view_test.exs │ ├── stripe_platform_customer_view_test.exs │ ├── task_list_view_test.exs │ ├── task_skill_view_test.exs │ ├── task_view_test.exs │ ├── token_view_test.exs │ ├── user_category_view_test.exs │ ├── user_role_view_test.exs │ ├── user_skill_view_test.exs │ ├── user_task_view_test.exs │ └── user_view_test.exs ├── support ├── api_case.ex ├── authentication_test_helpers.ex ├── channel_case.ex ├── conn_case.ex ├── db_access_case.ex ├── factories.ex ├── github │ ├── failure_api.ex │ ├── success_api.ex │ └── test_helpers.ex ├── json_api_helpers.ex ├── model_case.ex ├── policy_case.ex ├── stripe_case.ex ├── test_environment_helper.ex ├── test_helpers.ex └── view_case.ex └── test_helper.exs /.circleci/setup-heroku.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz 3 | sudo mkdir -p /usr/local/lib /usr/local/bin 4 | sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib 5 | sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku 6 | 7 | cat > ~/.netrc << EOF 8 | machine api.heroku.com 9 | login $HEROKU_LOGIN 10 | password $HEROKU_API_KEY 11 | EOF 12 | 13 | cat >> ~/.ssh/config << EOF 14 | VerifyHostKeyDNS yes 15 | StrictHostKeyChecking no 16 | EOF 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.apib] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | export AWS_ACCESS_KEY_ID= 2 | export AWS_SECRET_ACCESS_KEY= 3 | export CLOUDEX_API_KEY= 4 | export CLOUDEX_CLOUD_NAME= 5 | export CLOUDEX_SECRET= 6 | export CLOUDFRONT_DOMAIN= 7 | export GITHUB_APP_CLIENT_ID= 8 | export GITHUB_APP_CLIENT_SECRET= 9 | export GITHUB_APP_ID= 10 | export GITHUB_APP_PEM= 11 | export GITHUB_TEST_APP_CLIENT_ID= 12 | export GITHUB_TEST_APP_CLIENT_SECRET= 13 | export GITHUB_TEST_APP_ID= 14 | export GITHUB_TEST_APP_PEM= 15 | export INTERCOM_IDENTITY_SECRET_KEY= 16 | export POSTMARK_API_KEY= 17 | export S3_BUCKET= 18 | export SCOUT_APP_KEY= 19 | export SCOUT_APP_NAME= 20 | export SEGMENT_WRITE_KEY= 21 | export SENTRY_DSN= 22 | export STRIPE_SECRET_KEY= 23 | export STRIPE_PLATFORM_CLIENT_ID= 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Problem 2 | 3 | Detail the problem here, including any possible solutions. 4 | 5 | ## Subtasks 6 | - [ ] 7 | - [ ] 8 | - [ ] 9 | 10 | ## References 11 | 12 | Progress on: # 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # What's in this PR? 2 | 3 | List the changes you made and your reasons for them. 4 | 5 | Make sure any changes to code include changes to documentation. 6 | 7 | ## References 8 | Fixes # 9 | 10 | Progress on: # 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # App artifacts 2 | /_build 3 | /db 4 | /deps 5 | /*.ez 6 | /tmp 7 | /doc 8 | /bench 9 | 10 | # Generated on crash by the VM 11 | erl_crash.dump 12 | 13 | # Static artifacts 14 | /node_modules 15 | 16 | # The config/prod.secret.exs file by default contains sensitive 17 | # data and you should not commit it into version control. 18 | # 19 | # Alternatively, you may comment the line below and commit the 20 | # secrets file as long as you replace its contents by environment 21 | # variables. 22 | /config/prod.secret.exs 23 | 24 | # Secret 25 | .env 26 | 27 | /tmp 28 | 29 | .DS_Store 30 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: mix phoenix.server 2 | -------------------------------------------------------------------------------- /bin/deploy_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Ensure exit codes other than 0 fail the build 4 | set -e 5 | 6 | echo "Removing old local ./doc dir..." 7 | rm -rf ./doc 8 | 9 | # Pulling from GitHub 10 | echo "Pulling from GitHub..." 11 | mkdir -p doc 12 | cd doc 13 | git init 14 | if ! git remote | grep origin; then 15 | git remote add origin git@github.com:code-corps/code-corps-api-github-pages.git 16 | git fetch 17 | git checkout gh-pages 18 | fi 19 | cd .. 20 | 21 | # Generate docs 22 | echo "Generating docs..." 23 | mix docs 24 | 25 | # Push to GitHub 26 | echo "Checking GitHub..." 27 | cd doc 28 | git add . 29 | if git diff-index --quiet HEAD; 30 | then 31 | echo "Nothing to update." 32 | else 33 | echo "Pushing to GitHub..." 34 | git commit -m "Update docs" 35 | git push -u origin gh-pages:gh-pages --force 36 | fi 37 | 38 | # Exit successfully 39 | exit 0 40 | -------------------------------------------------------------------------------- /civic.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Code Corps", 3 | "description": "Help build and fund software projects with massive social impact.", 4 | "status": "Beta", 5 | "homepage": "https://www.codecorps.org", 6 | "license": "https://github.com/code-corps/code-corps-api/blob/develop/LICENSE.txt", 7 | "repository": "https://github.com/code-corps/code-corps-api", 8 | "geography": [], 9 | "contact": { 10 | "email": "josh@codecorps.org", 11 | "name": "Josh Smith" 12 | }, 13 | "type": "Elixir Phoenix Web Application", 14 | "tags": [ 15 | "developer tools", 16 | "donations", 17 | "fundraising", 18 | "open source", 19 | "project management", 20 | "software", 21 | "tech for good" 22 | ], 23 | "thumbnailUrl": "https://d3pgew4wbk2vb1.cloudfront.net/projects/1/thumb.png?v=1474089806", 24 | } 25 | -------------------------------------------------------------------------------- /config/scout_apm.exs: -------------------------------------------------------------------------------- 1 | # This configuration file is used for Scout APM. 2 | # See our help docs at http://help.apm.scoutapp.com for more information. 3 | # config/scout_apm.exs 4 | use Mix.Config 5 | 6 | config :scout_apm, 7 | name: {:system, "SCOUT_APP_NAME"}, # The app name that will appear within the Scout UI 8 | key: {:system, "SCOUT_APP_KEY"} 9 | 10 | config :phoenix, :template_engines, 11 | eex: ScoutApm.Instruments.EExEngine, 12 | exs: ScoutApm.Instruments.ExsEngine 13 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | # How to generate API documentation locally 2 | 3 | You can generate documentation a couple ways: 4 | 5 | - [Apiary Client](#apiary-client) (recommended) 6 | - [Atom](#atom) 7 | 8 | ### Apiary Client 9 | 10 | [Apiary Client](https://help.apiary.io/tools/apiary-cli/) needs Ruby to run. 11 | 12 | You can install the Apiary Client by running: 13 | 14 | ```shell 15 | gem install apiaryio 16 | ``` 17 | 18 | You can now run the server: 19 | 20 | ```shell 21 | apiary preview --path=blueprint/api.apib --server 22 | ``` 23 | 24 | This runs an Apiary CLI server on port `8080`. You can visit the documentation by visiting `localhost:8080` in your browser. Just refresh the page whenever you make changes to the documentation file at `/blueprint/api.apib`. 25 | 26 | ### Atom 27 | 28 | If you're developing with [Atom](https://atom.io/), you can also use the [API Blueprint Preview](https://atom.io/packages/api-blueprint-preview) package to preview your blueprint changes in realtime. 29 | -------------------------------------------------------------------------------- /elixir_buildpack.config: -------------------------------------------------------------------------------- 1 | erlang_version=20.2 2 | elixir_version=1.6.1 3 | -------------------------------------------------------------------------------- /lib/code_corps/accounts/users.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Accounts.Users do 2 | alias CodeCorps.ProjectUser 3 | 4 | import Ecto.Query 5 | 6 | def project_filter(query, %{"project_id" => project_id}) do 7 | from user in query, 8 | join: pu in ProjectUser, on: pu.user_id == user.id and pu.project_id == ^project_id 9 | end 10 | def project_filter(query, _), do: query 11 | end -------------------------------------------------------------------------------- /lib/code_corps/analytics/in_memory_api.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Analytics.InMemoryAPI do 2 | @moduledoc """ 3 | In-memory interface to simulate calling out to the Segment API. 4 | 5 | Each function should have the same signature as `CodeCorps.Analytics.SegmentAPI` and simply return `nil`. 6 | """ 7 | 8 | require Logger 9 | 10 | def identify(user_id, _traits), do: log_identify(user_id) 11 | 12 | def track(user_id, event_name, properties), do: log_track(user_id, event_name, properties) 13 | 14 | defp log_identify(user_id) do 15 | Logger.info "Called identify for User #{user_id}" 16 | end 17 | 18 | defp log_track(user_id, event_name, properties) do 19 | props = Poison.encode!(properties) 20 | Logger.info "Called track for event #{event_name} for User #{user_id} and properties #{props}" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/code_corps/analytics/segment_api.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Analytics.SegmentAPI do 2 | @moduledoc """ 3 | Interface to the Segment API through the [`analytics-elixir` package](https://github.com/stueccles/analytics-elixir). 4 | """ 5 | 6 | def identify(user_id, traits) do 7 | Segment.Analytics.identify(user_id, traits) 8 | end 9 | 10 | def track(user_id, event_name, properties) do 11 | Segment.Analytics.track(user_id, event_name, properties) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps/analytics/test_api.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Analytics.TestAPI do 2 | @moduledoc """ 3 | In-memory interface to simulate calling out to the Segment API, 4 | sending back itself a message with passed parameters - they're used for assertions. 5 | 6 | Each function should have the same signature as `CodeCorps.Analytics.SegmentAPI` and simply return `nil`. 7 | """ 8 | 9 | def identify(user_id, traits) do 10 | send self(), {:identify, user_id, traits} 11 | nil 12 | end 13 | 14 | def track(user_id, event_name, properties) do 15 | send self(), {:track, user_id, event_name, properties} 16 | nil 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps/auth/bearer_auth_pipeline.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Auth.BearerAuthPipeline do 2 | use Guardian.Plug.Pipeline, otp_app: :code_corps, 3 | module: CodeCorps.Guardian, 4 | error_handler: CodeCorps.Auth.ErrorHandler 5 | 6 | plug Guardian.Plug.VerifyHeader, realm: "Bearer" 7 | plug Guardian.Plug.LoadResource, allow_blank: true 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps/auth/ensure_auth_pipeline.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Auth.EnsureAuthPipeline do 2 | use Guardian.Plug.Pipeline, otp_app: :code_corps, 3 | module: CodeCorps.Guardian, 4 | error_handler: CodeCorps.Auth.ErrorHandler 5 | 6 | plug Guardian.Plug.EnsureAuthenticated 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/auth/error_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Auth.ErrorHandler do 2 | use CodeCorpsWeb, :controller 3 | 4 | def auth_error(conn, {type, _reason}, _opts) do 5 | conn 6 | |> put_status(401) 7 | |> render(CodeCorpsWeb.TokenView, "401.json", message: to_string(type)) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps/cloudex/cloudex_test.ex: -------------------------------------------------------------------------------- 1 | defmodule CloudexTest do 2 | @moduledoc """ 3 | Testing stub for `Cloudex`, 4 | 5 | Each function should have the same signature as `Cloudex`. 6 | """ 7 | 8 | defmodule Url do 9 | def for(_public_id, %{height: height, width: width}) do 10 | "https://placehold.it/#{width}x#{height}" 11 | end 12 | def for(_public_id, _options) do 13 | "https://placehold.it/500x500" 14 | end 15 | end 16 | 17 | @spec upload(String.t) :: {:ok, Cloudex.UploadedImage.t} 18 | def upload(_url) do 19 | {:ok, %Cloudex.UploadedImage{public_id: fake_cloudinary_id()}} 20 | end 21 | 22 | defp fake_cloudinary_id do 23 | :crypto.strong_rand_bytes(5) |> Base.encode64() 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/code_corps/cloudex/cloudinary_url.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Cloudex.CloudinaryUrl do 2 | @cloudex Application.get_env(:code_corps, :cloudex) 3 | 4 | def for(nil, _options, version, default_color, type) do 5 | "#{Application.get_env(:code_corps, :asset_host)}/icons/#{type}_default_#{version}_#{default_color}.png" 6 | end 7 | def for(public_id, options, _version, _default_color, _type) do 8 | @cloudex.Url.for(public_id, options) 9 | |> add_uri_scheme 10 | end 11 | 12 | defp add_uri_scheme(generated_url) do 13 | base_url = String.split(generated_url, "//") 14 | add_https(base_url) 15 | end 16 | 17 | defp add_https(base_url) when is_list(base_url) and length(base_url) > 0, do: "https://" <> List.last(base_url) 18 | defp add_https(url), do: url 19 | end 20 | -------------------------------------------------------------------------------- /lib/code_corps/cloudex/uploader.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Cloudex.Uploader do 2 | 3 | @cloudex Application.get_env(:code_corps, :cloudex) 4 | 5 | @spec upload(list | String.t) :: Cloudex.upload_result() 6 | def upload(list_or_url) do 7 | @cloudex.upload(list_or_url) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps/conn_utils.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.ConnUtils do 2 | def extract_ip(%Plug.Conn{} = conn) do 3 | conn.remote_ip |> Tuple.to_list |> Enum.join(".") 4 | end 5 | 6 | def extract_user_agent(%Plug.Conn{} = conn) do 7 | conn |> Plug.Conn.get_req_header("user-agent") |> List.first 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps/emails/base_email.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.BaseEmail do 2 | import Bamboo.Email, only: [from: 2, new_email: 0] 3 | alias CodeCorps.User 4 | 5 | @spec create :: Bamboo.Email.t 6 | def create do 7 | new_email() 8 | |> from("Code Corps") 9 | end 10 | 11 | @spec get_name(User.t) :: String.t 12 | def get_name(%User{first_name: nil}), do: "there" 13 | def get_name(%User{first_name: name}), do: name 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps/emails/forgot_password_email.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.ForgotPasswordEmail do 2 | import Bamboo.Email, only: [to: 2] 3 | import Bamboo.PostmarkHelper 4 | 5 | alias CodeCorps.{Emails.BaseEmail, User, WebClient} 6 | 7 | @spec create(User.t, String.t) :: Bamboo.Email.t 8 | def create(%User{} = user, token) do 9 | BaseEmail.create 10 | |> to(user.email) 11 | |> template(template_id(), %{link: link(token)}) 12 | end 13 | 14 | @spec template_id :: String.t 15 | defp template_id, do: Application.get_env(:code_corps, :postmark_forgot_password_template) 16 | 17 | @spec link(String.t) :: String.t 18 | defp link(token) do 19 | WebClient.url() 20 | |> URI.merge("password/reset?token=#{token}") 21 | |> URI.to_string 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/code_corps/github/adapters/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Adapters.Repo do 2 | 3 | @mapping [ 4 | {:github_account_avatar_url, ["owner", "avatar_url"]}, 5 | {:github_account_id, ["owner", "id"]}, 6 | {:github_account_login, ["owner", "login"]}, 7 | {:github_account_type, ["owner", "type"]}, 8 | {:github_id, ["id"]}, 9 | {:name, ["name"]} 10 | ] 11 | 12 | @spec from_api(map) :: map 13 | def from_api(%{} = payload) do 14 | payload |> CodeCorps.Adapter.MapTransformer.transform(@mapping) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/code_corps/github/api/gateway.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.API.Gateway do 2 | @moduledoc ~S""" 3 | The gate through which all communication with the GitHub API must go through. 4 | 5 | The purpose of this module is to centralize the most basic GitHub API request, 6 | so the module can be injected into tests easily, giving full control to what 7 | the tested response is. 8 | """ 9 | 10 | alias CodeCorps.GitHub 11 | 12 | @spec request(GitHub.method, String.t, GitHub.body, GitHub.headers, list) :: GitHub.response 13 | def request(method, url, body, headers, options) do 14 | HTTPoison.request(method, url, body, headers, options) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/code_corps/github/api/pull_request.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.API.PullRequest do 2 | @moduledoc ~S""" 3 | Functions for working with pull requests on GitHub. 4 | """ 5 | 6 | alias CodeCorps.{ 7 | GitHub, 8 | GithubAppInstallation, 9 | GithubRepo 10 | } 11 | 12 | @doc """ 13 | Fetches a pull request from the GitHub API, given the API URL for the pull 14 | request and the `CodeCorps.GithubRepo` record that points to its GitHub 15 | repository. 16 | """ 17 | def from_url(url, %GithubRepo{github_app_installation: %GithubAppInstallation{} = installation}) do 18 | "https://api.github.com/" <> endpoint = url 19 | 20 | with opts when is_list(opts) <- GitHub.API.opts_for(installation) do 21 | GitHub.request(:get, endpoint, %{}, %{}, opts) 22 | else 23 | {:error, github_error} -> {:error, github_error} 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/handler.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Handler do 2 | @moduledoc ~S""" 3 | Default behavior for all GitHub webhook event handlers. 4 | """ 5 | 6 | @doc ~S""" 7 | The only entry point a GitHub webhook event handler function should contain. 8 | 9 | Receives the GitHub payload, returns an `:ok` tuple if the process was 10 | successful, or an `:error` tuple, where the second element is an atom, if it 11 | failed. 12 | """ 13 | @callback handle(map) :: {:ok, any} | {:error, atom} | {:error, atom, any} 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/installation/validator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Installation.Validator do 2 | @moduledoc ~S""" 3 | In charge of validatng a GitHub.API.Installation webhook payload. 4 | 5 | https://developer.github.com/v3/activity/events/types/#installationevent 6 | """ 7 | 8 | @behaviour CodeCorps.GitHub.Event.Validator 9 | 10 | @doc ~S""" 11 | Returns `true` if all keys required to properly handle an Installation webhook 12 | are present in the provided payload. 13 | """ 14 | @impl CodeCorps.GitHub.Event.Validator 15 | @spec valid?(map) :: boolean 16 | def valid?(%{ 17 | "action" => _, 18 | "installation" => %{ 19 | "id" => _, 20 | "account" => %{ 21 | "id" => _ 22 | } 23 | }, 24 | "sender" => %{ 25 | "id" => _ 26 | } 27 | }), do: true 28 | def valid?(_), do: false 29 | end 30 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/issue_comment/validator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.IssueComment.Validator do 2 | @moduledoc ~S""" 3 | In charge of validatng a GitHub IssueComment webhook payload. 4 | 5 | https://developer.github.com/v3/activity/events/types/#issuecommentevent 6 | """ 7 | 8 | @behaviour CodeCorps.GitHub.Event.Validator 9 | 10 | @doc ~S""" 11 | Returns `true` if all keys required to properly handle an Issue webhook are 12 | present in the provided payload. 13 | """ 14 | @impl CodeCorps.GitHub.Event.Validator 15 | @spec valid?(map) :: boolean 16 | def valid?(%{ 17 | "action" => _, 18 | "issue" => %{ 19 | "id" => _, "title" => _, "body" => _, "state" => _, 20 | "user" => %{"id" => _} 21 | }, 22 | "comment" => %{ 23 | "id" => _, "body" => _, 24 | "user" => %{"id" => _} 25 | }, 26 | "repository" => %{"id" => _}}), do: true 27 | def valid?(_), do: false 28 | end 29 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/issues/validator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Issues.Validator do 2 | @moduledoc ~S""" 3 | In charge of validatng a GitHub Issue webhook payload. 4 | 5 | https://developer.github.com/v3/activity/events/types/#issuesevent 6 | """ 7 | 8 | @behaviour CodeCorps.GitHub.Event.Validator 9 | 10 | @doc ~S""" 11 | Returns `true` if all keys required to properly handle an Issue webhook are 12 | present in the provided payload. 13 | """ 14 | @impl CodeCorps.GitHub.Event.Validator 15 | @spec valid?(map) :: boolean 16 | def valid?(%{ 17 | "action" => _, 18 | "issue" => %{ 19 | "id" => _, 20 | "title" => _, 21 | "body" => _, 22 | "state" => _, 23 | "user" => %{ 24 | "id" => _ 25 | } 26 | }, 27 | "repository" => %{ 28 | "id" => _ 29 | } 30 | }), do: true 31 | def valid?(_), do: false 32 | end 33 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/pull_request/validator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.PullRequest.Validator do 2 | @moduledoc ~S""" 3 | In charge of validatng a GitHub PullRequest webhook payload. 4 | 5 | https://developer.github.com/v3/activity/events/types/#pullrequestevent 6 | """ 7 | 8 | @behaviour CodeCorps.GitHub.Event.Validator 9 | 10 | @doc ~S""" 11 | Returns `true` if all keys required to properly handle an PullRequest webhook 12 | are present in the provided payload. 13 | """ 14 | @impl CodeCorps.GitHub.Event.Validator 15 | @spec valid?(map) :: boolean 16 | def valid?(%{ 17 | "action" => _, 18 | "pull_request" => %{ 19 | "id" => _, 20 | "title" => _, 21 | "user" => %{ 22 | "id" => _ 23 | } 24 | }, 25 | "repository" => %{ 26 | "id" => _ 27 | } 28 | }), do: true 29 | def valid?(_), do: false 30 | end 31 | -------------------------------------------------------------------------------- /lib/code_corps/github/event/validator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Validator do 2 | @moduledoc ~S""" 3 | Default behavior for all GitHub webhook event payload validators. 4 | """ 5 | 6 | @doc ~S""" 7 | The only entry point a GitHub webhook event validator function should contain. 8 | 9 | Receives the GitHub payload, returns `true` if the payload is in the expected 10 | format, `false` otherwise. 11 | """ 12 | @callback valid?(map) :: boolean 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps/github/sync/github_pull_request/body_parser.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Sync.GithubPullRequest.BodyParser do 2 | @moduledoc ~S""" 3 | In charge of extracting ids from markdown content, paired to a predefined list 4 | of keywords. 5 | """ 6 | 7 | @doc ~S""" 8 | Searches for GitHub closing keyword format inside a content string. 9 | Returns all unique ids matched, as integers. 10 | """ 11 | @spec extract_closing_ids(String.t) :: list(integer) 12 | def extract_closing_ids(content) when is_binary(content) do 13 | ~w(close closes closed fix fixes fixed resolve resolves resolved) 14 | |> matching_regex() 15 | |> Regex.scan(content) # [["closes #1", "closes", "1"], ["fixes #2", "fixes", "2"]] 16 | |> Enum.map(&List.last/1) # ["1", "2"] 17 | |> Enum.map(&String.to_integer/1) # [1, 2] 18 | |> Enum.uniq 19 | end 20 | 21 | defp matching_regex(keywords) do 22 | matches = keywords |> Enum.join("|") 23 | ~r/(?:(#{matches}))\s+#(\d+)/i 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/code_corps/github/sync/github_user/changeset.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Sync.GithubUser.Changeset do 2 | @moduledoc ~S""" 3 | In charge of changesets for actions on `CodeCorps.GithubUser` records. 4 | """ 5 | 6 | alias CodeCorps.GithubUser 7 | alias Ecto.Changeset 8 | 9 | @doc ~S""" 10 | Builds a changeset for creating or updating a `CodeCorps.GithubUser` record. 11 | """ 12 | @spec changeset(GithubUser.t(), map) :: Changeset.t() 13 | def changeset(%GithubUser{} = struct, %{} = attrs) do 14 | struct 15 | |> Changeset.cast(attrs, [:avatar_url, :email, :github_id, :username, :type]) 16 | |> Changeset.validate_required([:avatar_url, :github_id, :username, :type]) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps/guardian.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Guardian do 2 | use Guardian, otp_app: :code_corps 3 | 4 | alias CodeCorps.{Project, Repo, User} 5 | 6 | def subject_for_token(project = %Project{}, _claims) do 7 | {:ok, "Project:#{project.id}"} 8 | end 9 | def subject_for_token(user = %User{}, _claims) do 10 | {:ok, "User:#{user.id}"} 11 | end 12 | def subject_for_token(_, _) do 13 | {:error, :unknown_resource_type} 14 | end 15 | 16 | def resource_from_claims(%{"sub" => sub}), do: resource_from_subject(sub) 17 | def resource_from_claims(_), do: {:error, :missing_subject} 18 | 19 | defp resource_from_subject("Project:" <> id), do: {:ok, Repo.get(Project, id)} 20 | defp resource_from_subject("User:" <> id), do: {:ok, Repo.get(User, id)} 21 | defp resource_from_subject(_), do: {:error, :unknown_resource_type} 22 | end 23 | -------------------------------------------------------------------------------- /lib/code_corps/helpers/random_icon_color.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Helpers.RandomIconColor do 2 | alias Ecto.Changeset 3 | 4 | @icon_color_generator Application.get_env(:code_corps, :icon_color_generator) 5 | 6 | def generate_icon_color(changeset, icon_color_key) do 7 | case changeset do 8 | %Changeset{valid?: true, changes: _changes} -> 9 | Changeset.put_change(changeset, icon_color_key, @icon_color_generator.generate()) 10 | _ -> 11 | changeset 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps/helpers/slug.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Helpers.Slug do 2 | alias Ecto.Changeset 3 | 4 | def generate_slug(changeset, value_key, slug_key) do 5 | case changeset do 6 | %Changeset{valid?: true, changes: changes} -> 7 | case Map.fetch(changes, value_key) do 8 | {:ok, value} -> Changeset.put_change(changeset, slug_key, Inflex.parameterize(value)) 9 | _ -> changeset 10 | end 11 | _ -> 12 | changeset 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/helpers/string.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Helpers.String do 2 | def coalesce_id_string(string) do 3 | string 4 | |> String.split(",") 5 | |> Enum.map(&String.to_integer(&1)) 6 | end 7 | 8 | def coalesce_string(string) do 9 | string 10 | |> String.split(",") 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps/helpers/url.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Helpers.URL do 2 | @moduledoc """ 3 | Provides some helpers for assembling and validating URLs. 4 | """ 5 | 6 | alias Ecto.Changeset 7 | 8 | @doc """ 9 | Prefixes the URL with `http://` in the event that `http://` and `https://` are 10 | not already the starting format. If `nil`, simply returns `nil`. 11 | """ 12 | def prefix_url(changeset, key) do 13 | changeset 14 | |> Changeset.update_change(key, &do_prefix_url/1) 15 | end 16 | 17 | defp do_prefix_url(nil), do: nil 18 | defp do_prefix_url("http://" <> rest), do: "http://" <> rest 19 | defp do_prefix_url("https://" <> rest), do: "https://" <> rest 20 | defp do_prefix_url(value), do: "http://" <> value 21 | 22 | def valid_format do 23 | ~r/\A((http|https):\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}(([0-9]{1,5})?\/.*)?#=\z/ix 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/code_corps/mailer.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Mailer do 2 | use Bamboo.Mailer, otp_app: :code_corps 3 | end 4 | -------------------------------------------------------------------------------- /lib/code_corps/model/auth_token.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.AuthToken do 2 | @moduledoc """ 3 | Represents one of the user's many possible authentication tokens, created 4 | using `Phoenix.Token.sign/4`. 5 | 6 | Many can coexist and be valid at the same time. They can be used for 7 | password resets or passwordless logins. 8 | 9 | These tokens do expire based on the `max_age` value passed to 10 | `Phoenix.Token.verify/4`. 11 | """ 12 | 13 | use CodeCorps.Model 14 | 15 | schema "auth_token" do 16 | field :value, :string 17 | 18 | belongs_to :user, CodeCorps.User 19 | 20 | timestamps() 21 | end 22 | 23 | @doc """ 24 | Builds a changeset based on the `struct` 25 | """ 26 | def changeset(struct, user) do 27 | token = CodeCorpsWeb.Endpoint |> Phoenix.Token.sign("user", user.id) 28 | struct 29 | |> cast(%{ value: token, user_id: user.id }, [:value, :user_id]) 30 | |> validate_required([:value, :user_id]) 31 | |> assoc_constraint(:user) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/code_corps/model/github_issue_assignee.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GithubIssueAssignee do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | 6 | schema "github_issue_assignees" do 7 | belongs_to :github_issue, CodeCorps.GithubIssue 8 | belongs_to :github_user, CodeCorps.GithubUser 9 | 10 | timestamps() 11 | end 12 | 13 | @doc false 14 | def changeset(struct, attrs) do 15 | struct 16 | |> cast(attrs, [:github_issue_id, :github_user_id]) 17 | |> validate_required([:github_issue_id, :github_user_id]) 18 | |> assoc_constraint(:github_issue) 19 | |> assoc_constraint(:github_user) 20 | |> unique_constraint(:github_user, name: :github_issue_assignees_github_issue_id_github_user_id_index) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/code_corps/model/github_user.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GithubUser do 2 | use Ecto.Schema 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "github_users" do 7 | field :avatar_url, :string 8 | field :email, :string 9 | field :github_id, :integer 10 | field :type, :string 11 | field :username, :string 12 | 13 | has_one :user, CodeCorps.User 14 | 15 | timestamps() 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/code_corps/model/model.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Model do 2 | @moduledoc ~S""" 3 | A temporary module to be used by existing Model modules, before we switch to 4 | an intent based structure which Phoenix 1.3 pushes. 5 | """ 6 | 7 | @doc ~S""" 8 | When used import appropriate helper modules. 9 | """ 10 | defmacro __using__(_opts) do 11 | quote do 12 | use Ecto.Schema 13 | 14 | import Ecto 15 | import Ecto.Changeset 16 | import Ecto.Query 17 | 18 | use Timex.Ecto.Timestamps 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/code_corps/model/organization_github_app_installation.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.OrganizationGithubAppInstallation do 2 | use CodeCorps.Model 3 | 4 | schema "organization_github_app_installations" do 5 | belongs_to :github_app_installation, CodeCorps.GithubAppInstallation 6 | belongs_to :organization, CodeCorps.Organization 7 | 8 | timestamps() 9 | end 10 | 11 | @doc """ 12 | Builds a changeset based on the `struct` and `params`. 13 | """ 14 | def create_changeset(struct, params \\ %{}) do 15 | struct 16 | |> changeset(params) 17 | end 18 | 19 | defp changeset(struct, params) do 20 | struct 21 | |> cast(params, [:github_app_installation_id, :organization_id]) 22 | |> validate_required([:github_app_installation_id, :organization_id]) 23 | |> assoc_constraint(:github_app_installation, name: "organization_github_app_installations_github_app_installation_i") 24 | |> assoc_constraint(:organization) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/code_corps/model/preview.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Preview do 2 | @moduledoc """ 3 | Represents an category on Code Corps, e.g. "Society" and "Technology". 4 | """ 5 | 6 | use CodeCorps.Model 7 | alias CodeCorps.Services.MarkdownRendererService 8 | 9 | @type t :: %__MODULE__{} 10 | 11 | schema "previews" do 12 | field :body, :string 13 | field :markdown, :string 14 | 15 | belongs_to :user, CodeCorps.User 16 | 17 | timestamps() 18 | end 19 | 20 | @doc """ 21 | Builds a changeset based on the `struct` and `params`. 22 | """ 23 | def create_changeset(struct, params \\ %{}) do 24 | struct 25 | |> cast(params, [:markdown, :user_id]) 26 | |> validate_required([:markdown, :user_id]) 27 | |> assoc_constraint(:user) 28 | |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/code_corps/model/project_category.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.ProjectCategory do 2 | @moduledoc """ 3 | Represents a category of a project. 4 | """ 5 | 6 | use CodeCorps.Model 7 | 8 | @type t :: %__MODULE__{} 9 | 10 | schema "project_categories" do 11 | belongs_to :project, CodeCorps.Project 12 | belongs_to :category, CodeCorps.Category 13 | 14 | timestamps() 15 | end 16 | 17 | @doc """ 18 | Builds a changeset based on the `struct` and `params`, for creating a record. 19 | """ 20 | def create_changeset(struct, params \\ %{}) do 21 | struct 22 | |> cast(params, [:project_id, :category_id]) 23 | |> validate_required([:project_id, :category_id]) 24 | |> assoc_constraint(:project) 25 | |> assoc_constraint(:category) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/code_corps/model/project_skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.ProjectSkill do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "project_skills" do 7 | belongs_to :project, CodeCorps.Project 8 | belongs_to :skill, CodeCorps.Skill 9 | 10 | timestamps() 11 | end 12 | 13 | @doc """ 14 | Builds a changeset based on the `struct` and `params`. 15 | """ 16 | def create_changeset(struct, params \\ %{}) do 17 | struct 18 | |> cast(params, [:project_id, :skill_id]) 19 | |> validate_required([:project_id, :skill_id]) 20 | |> assoc_constraint(:project) 21 | |> assoc_constraint(:skill) 22 | |> unique_constraint(:project_id, name: :index_projects_on_project_id_skill_id) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/model/skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Skill do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "skills" do 7 | field :description, :string 8 | field :original_row, :integer 9 | field :title, :string 10 | 11 | has_many :project_skills, CodeCorps.ProjectSkill 12 | has_many :projects, through: [:project_skills, :project] 13 | 14 | has_many :role_skills, CodeCorps.RoleSkill 15 | has_many :roles, through: [:role_skills, :role] 16 | 17 | timestamps() 18 | end 19 | 20 | @doc """ 21 | Builds a changeset based on the `struct` and `params`. 22 | """ 23 | @spec changeset(CodeCorps.Skill.t, map) :: Ecto.Changeset.t 24 | def changeset(struct, params \\ %{}) do 25 | struct 26 | |> cast(params, [:description, :original_row, :title]) 27 | |> validate_required([:title]) 28 | |> unique_constraint(:title) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/code_corps/model/stripe_connect_card.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeConnectCard do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "stripe_connect_cards" do 7 | field :id_from_stripe, :string, null: false 8 | 9 | belongs_to :stripe_connect_account, CodeCorps.StripeConnectAccount 10 | belongs_to :stripe_platform_card, CodeCorps.StripePlatformCard 11 | 12 | timestamps() 13 | end 14 | 15 | def create_changeset(struct, params \\ %{}) do 16 | struct 17 | |> cast(params, [:id_from_stripe, :stripe_connect_account_id, :stripe_platform_card_id]) 18 | |> validate_required([:id_from_stripe, :stripe_connect_account_id, :stripe_platform_card_id]) 19 | |> assoc_constraint(:stripe_connect_account) 20 | |> assoc_constraint(:stripe_platform_card) 21 | |> unique_constraint(:id_from_stripe) 22 | |> unique_constraint(:stripe_connect_account_id, name: :index_projects_on_user_id_role_id) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/model/stripe_file_upload.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeFileUpload do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "stripe_file_uploads" do 7 | field :created, :integer 8 | field :id_from_stripe, :string, null: false 9 | field :purpose, :string 10 | field :size, :integer 11 | field :type, :string 12 | field :url, :string 13 | 14 | belongs_to :stripe_connect_account, CodeCorps.StripeConnectAccount 15 | 16 | timestamps() 17 | end 18 | 19 | def create_changeset(struct, params \\ %{}) do 20 | struct 21 | |> cast(params, [:created, :id_from_stripe, :purpose, :size, :type, :url, :stripe_connect_account_id]) 22 | |> validate_required([:id_from_stripe]) 23 | |> assoc_constraint(:stripe_connect_account) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/code_corps/model/stripe_platform_customer.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripePlatformCustomer do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "stripe_platform_customers" do 7 | field :created, :integer 8 | field :currency, :string 9 | field :delinquent, :boolean 10 | field :email, :string 11 | field :id_from_stripe, :string, null: false 12 | 13 | belongs_to :user, CodeCorps.User 14 | 15 | has_many :stripe_connect_customers, CodeCorps.StripeConnectCustomer 16 | 17 | timestamps() 18 | end 19 | 20 | def create_changeset(struct, params \\ %{}) do 21 | struct 22 | |> cast(params, [:created, :currency, :delinquent, :id_from_stripe, :user_id]) 23 | |> validate_required([:id_from_stripe, :user_id]) 24 | |> assoc_constraint(:user) 25 | end 26 | 27 | def update_changeset(struct, params) do 28 | struct 29 | |> cast(params, [:email]) 30 | |> validate_required([:email]) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/code_corps/model/user_category.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.UserCategory do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "user_categories" do 7 | belongs_to :user, CodeCorps.User 8 | belongs_to :category, CodeCorps.Category 9 | 10 | timestamps() 11 | end 12 | 13 | @doc """ 14 | Builds a changeset based on the `struct` and `params`. 15 | """ 16 | def create_changeset(struct, params \\ %{}) do 17 | struct 18 | |> cast(params, [:user_id, :category_id]) 19 | |> validate_required([:user_id, :category_id]) 20 | |> assoc_constraint(:user) 21 | |> assoc_constraint(:category) 22 | |> unique_constraint(:user_id, name: :index_projects_on_user_id_category_id) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/model/user_role.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.UserRole do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "user_roles" do 7 | belongs_to :user, CodeCorps.User 8 | belongs_to :role, CodeCorps.Role 9 | 10 | timestamps() 11 | end 12 | 13 | @doc """ 14 | Builds a changeset based on the `struct` and `params`. 15 | """ 16 | def create_changeset(struct, params \\ %{}) do 17 | struct 18 | |> cast(params, [:user_id, :role_id]) 19 | |> validate_required([:user_id, :role_id]) 20 | |> assoc_constraint(:user) 21 | |> assoc_constraint(:role) 22 | |> unique_constraint(:user_id, name: :index_projects_on_user_id_role_id) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/model/user_skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.UserSkill do 2 | use CodeCorps.Model 3 | 4 | @type t :: %__MODULE__{} 5 | 6 | schema "user_skills" do 7 | belongs_to :user, CodeCorps.User 8 | belongs_to :skill, CodeCorps.Skill 9 | 10 | timestamps() 11 | end 12 | 13 | @doc """ 14 | Builds a changeset based on the `struct` and `params`. 15 | """ 16 | def create_changeset(struct, params \\ %{}) do 17 | struct 18 | |> cast(params, [:user_id, :skill_id]) 19 | |> validate_required([:user_id, :skill_id]) 20 | |> assoc_constraint(:user) 21 | |> assoc_constraint(:skill) 22 | |> unique_constraint(:user_id, name: :index_projects_on_user_id_skill_id) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/policy/category.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Category do 2 | alias CodeCorps.User 3 | 4 | @spec create?(User.t) :: boolean 5 | def create?(%User{admin: true}), do: true 6 | def create?(%User{}), do: false 7 | 8 | @spec update?(User.t) :: boolean 9 | def update?(%User{admin: true}), do: true 10 | def update?(%User{}), do: false 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps/policy/comment.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Comment do 2 | @moduledoc ~S""" 3 | Authorization policies for performing actions on `Comment` records 4 | """ 5 | alias CodeCorps.{Comment, User} 6 | 7 | def create?(%User{id: user_id}, %{"user_id" => author_id}) 8 | when user_id == author_id and not is_nil(user_id), do: true 9 | def create?(%User{}, %{}), do: false 10 | 11 | def update?(%User{id: user_id}, %Comment{user_id: author_id}) 12 | when user_id == author_id and not is_nil(user_id), do: true 13 | def update?(%User{}, %Comment{}), do: false 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps/policy/donation_goal.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.DonationGoal do 2 | 3 | import CodeCorps.Policy.Helpers, only: [get_project: 1, owned_by?: 2] 4 | 5 | alias CodeCorps.{DonationGoal, User} 6 | 7 | @spec create?(User.t, map) :: boolean 8 | def create?(%User{} = user, %{} = params), 9 | do: params |> get_project |> owned_by?(user) 10 | 11 | @spec update?(User.t, DonationGoal.t) :: boolean 12 | def update?(%User{} = user, %DonationGoal{} = donation_goal), 13 | do: donation_goal |> get_project |> owned_by?(user) 14 | 15 | @spec delete?(User.t, DonationGoal.t) :: boolean 16 | def delete?(%User{} = user, %DonationGoal{} = donation_goal), 17 | do: donation_goal |> get_project |> owned_by?(user) 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps/policy/github_app_installation.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.GithubAppInstallation do 2 | @moduledoc """ 3 | Handles `User` authorization of actions on `GithubAppInstallation` records 4 | """ 5 | import CodeCorps.Policy.Helpers, only: [get_project: 1, owned_by?: 2] 6 | 7 | alias CodeCorps.User 8 | 9 | @spec create?(User.t, map) :: boolean 10 | def create?(%User{} = user, params), do: params |> get_project |> owned_by?(user) 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps/policy/github_event.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.GithubEvent do 2 | alias CodeCorps.User 3 | 4 | def index?(%User{admin: true}), do: true 5 | def index?(%User{admin: false}), do: false 6 | 7 | def show?(%User{admin: true}), do: true 8 | def show?(%User{admin: false}), do: false 9 | 10 | def update?(%User{admin: true}), do: true 11 | def update?(%User{admin: false}), do: false 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps/policy/github_repo.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.GithubRepo do 2 | @moduledoc """ 3 | Handles `User` authorization of actions on `GithubRepo` records 4 | """ 5 | import CodeCorps.Policy.Helpers, only: [get_project: 1, administered_by?: 2] 6 | 7 | alias CodeCorps.{GithubRepo, User} 8 | 9 | def update?(%User{} = user, %GithubRepo{project_id: nil}, %{"project_id" => _} = params) do 10 | params |> get_project |> administered_by?(user) 11 | end 12 | def update?(%User{} = user, %GithubRepo{} = github_repo, %{}) do 13 | github_repo |> get_project |> administered_by?(user) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/policy/organization_github_app_installation.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.OrganizationGithubAppInstallation do 2 | @moduledoc """ 3 | Handles `User` authorization of actions on `OrganizationGithubAppInstallation` records 4 | """ 5 | import CodeCorps.Policy.Helpers, only: [get_organization: 1, owned_by?: 2] 6 | 7 | alias CodeCorps.{OrganizationGithubAppInstallation, User} 8 | 9 | def create?(%User{} = user, params) do 10 | params |> get_organization |> owned_by?(user) 11 | end 12 | 13 | def delete?(%User{} = user, %OrganizationGithubAppInstallation{} = github_app_installation), 14 | do: github_app_installation |> get_organization |> owned_by?(user) 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/policy/organization_invite.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.OrganizationInvite do 2 | alias CodeCorps.User 3 | 4 | def create?(%User{admin: true}), do: true 5 | def create?(%User{admin: false}), do: false 6 | 7 | def update?(%User{admin: true}), do: true 8 | def update?(%User{admin: false}), do: false 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps/policy/preview.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Preview do 2 | alias CodeCorps.User 3 | 4 | @spec create?(User.t, map) :: boolean 5 | def create?(%User{} = user, %{"user_id" => author_id}), do: user.id == author_id 6 | def create?(%User{}, %{}), do: false 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/policy/project.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Project do 2 | import CodeCorps.Policy.Helpers, 3 | only: [get_organization: 1, owned_by?: 2, administered_by?: 2] 4 | 5 | alias CodeCorps.{Project, User} 6 | 7 | @spec create?(User.t, map) :: boolean 8 | def create?(%User{} = user, params) do 9 | params |> get_organization() |> owned_by?(user) 10 | end 11 | 12 | @spec update?(User.t, Project.t, map) :: boolean 13 | def update?(%User{admin: true}, %Project{}, %{}), do: true 14 | def update?(%User{}, %Project{}, %{"approved" => true}), do: false 15 | def update?(%User{} = user, %Project{} = project, _), do: project |> administered_by?(user) 16 | end 17 | -------------------------------------------------------------------------------- /lib/code_corps/policy/project_category.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.ProjectCategory do 2 | import CodeCorps.Policy.Helpers, only: [get_project: 1, administered_by?: 2] 3 | 4 | alias CodeCorps.{ProjectCategory, User} 5 | 6 | @spec create?(User.t, map) :: boolean 7 | def create?(%User{} = user, %{} = params) do 8 | params |> get_project |> administered_by?(user) 9 | end 10 | 11 | @spec delete?(User.t, ProjectCategory.t) :: boolean 12 | def delete?(%User{} = user, %ProjectCategory{} = project_category) do 13 | project_category |> get_project |> administered_by?(user) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/policy/project_skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.ProjectSkill do 2 | import CodeCorps.Policy.Helpers, only: [get_project: 1, administered_by?: 2] 3 | 4 | alias CodeCorps.{ProjectSkill, User} 5 | 6 | @spec create?(User.t, map) :: boolean 7 | def create?(%User{} = user, %{} = params) do 8 | params |> get_project |> administered_by?(user) 9 | end 10 | 11 | @spec delete?(User.t, ProjectSkill.t) :: boolean 12 | def delete?(%User{} = user, %ProjectSkill{} = project_skill) do 13 | project_skill |> get_project |> administered_by?(user) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/policy/role.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Role do 2 | alias CodeCorps.User 3 | 4 | def create?(%User{admin: true}), do: true 5 | def create?(%User{admin: false}), do: false 6 | end 7 | -------------------------------------------------------------------------------- /lib/code_corps/policy/role_skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.RoleSkill do 2 | alias CodeCorps.User 3 | 4 | @spec create?(User.t) :: boolean 5 | def create?(%User{admin: true}), do: true 6 | def create?(%User{admin: false}), do: false 7 | 8 | @spec delete?(User.t) :: boolean 9 | def delete?(%User{admin: true}), do: true 10 | def delete?(%User{admin: false}), do: false 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps/policy/skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Skill do 2 | alias CodeCorps.User 3 | 4 | def create?(%User{admin: true}), do: true 5 | def create?(%User{admin: false}), do: false 6 | end 7 | -------------------------------------------------------------------------------- /lib/code_corps/policy/stripe_connect_account.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.StripeConnectAccount do 2 | import CodeCorps.Policy.Helpers, only: [get_organization: 1, owned_by?: 2] 3 | 4 | alias CodeCorps.{StripeConnectAccount, User} 5 | 6 | def show?(%User{} = user, %StripeConnectAccount{} = stripe_connect_account), 7 | do: stripe_connect_account |> get_organization() |> owned_by?(user) 8 | 9 | def create?(%User{} = user, %{} = params), 10 | do: params |> get_organization() |> owned_by?(user) 11 | 12 | def update?(%User{} = user, %StripeConnectAccount{} = stripe_connect_account), 13 | do: stripe_connect_account |> get_organization() |> owned_by?(user) 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps/policy/stripe_connect_plan.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.StripeConnectPlan do 2 | import CodeCorps.Policy.Helpers, only: [get_project: 1, owned_by?: 2] 3 | 4 | alias CodeCorps.{StripeConnectPlan, User} 5 | 6 | @spec show?(User.t, StripeConnectPlan.t) :: boolean 7 | def show?(%User{} = user, %StripeConnectPlan{} = plan) do 8 | plan |> get_project |> owned_by?(user) 9 | end 10 | 11 | @spec create?(User.t, map) :: boolean 12 | def create?(%User{} = user, %{} = params) do 13 | params |> get_project |> owned_by?(user) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/policy/stripe_connect_subscription.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.StripeConnectSubscription do 2 | alias CodeCorps.{StripeConnectSubscription, User} 3 | 4 | @spec create?(User.t, map) :: boolean 5 | def create?(user, params), do: user |> owns?(params) 6 | 7 | @spec show?(User.t, StripeConnectSubscription.t) :: boolean 8 | def show?(user, subscription), do: user |> owns?(subscription) 9 | 10 | defp owns?(%User{id: current_user_id}, %StripeConnectSubscription{user_id: user_id}) do 11 | current_user_id == user_id 12 | end 13 | defp owns?(%User{id: current_user_id}, %{"user_id" => user_id}) do 14 | current_user_id == user_id 15 | end 16 | defp owns?(_, _), do: false 17 | end 18 | -------------------------------------------------------------------------------- /lib/code_corps/policy/stripe_platform_card.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.StripePlatformCard do 2 | alias CodeCorps.StripePlatformCard 3 | alias CodeCorps.User 4 | 5 | @spec create?(User.t, map) :: boolean 6 | def create?(user, params), do: user |> owns?(params) 7 | 8 | @spec show?(User.t, StripePlatformCard.t) :: boolean 9 | def show?(user, card), do: user |> owns?(card) 10 | 11 | @spec owns?(User.t, StripePlatformCard.t | map) :: boolean 12 | defp owns?(%User{id: current_user_id}, %StripePlatformCard{user_id: user_id}) do 13 | current_user_id == user_id 14 | end 15 | 16 | defp owns?(%User{id: current_user_id}, %{"user_id" => user_id}) do 17 | current_user_id == user_id 18 | end 19 | 20 | defp owns?(_, _), do: false 21 | end 22 | -------------------------------------------------------------------------------- /lib/code_corps/policy/stripe_platform_customer.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.StripePlatformCustomer do 2 | alias CodeCorps.StripePlatformCustomer 3 | alias CodeCorps.User 4 | 5 | def create?(%User{id: current_user_id}, %{"user_id" => user_id}), do: current_user_id == user_id 6 | def create?(%User{}, %{}), do: false 7 | 8 | def show?(%User{admin: true}, %StripePlatformCustomer{}), do: true 9 | def show?(%User{id: current_user_id}, %StripePlatformCustomer{user_id: user_id}), do: current_user_id == user_id 10 | def show?(%User{}, %StripePlatformCustomer{}), do: false 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps/policy/task.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.Task do 2 | @moduledoc ~S""" 3 | Authorization policy in charge of determining if a `User` is authorized to 4 | perform an action on a `Task`. 5 | """ 6 | import CodeCorps.Policy.Helpers, 7 | only: [get_project: 1, administered_by?: 2, task_authored_by?: 2] 8 | 9 | alias CodeCorps.{Task, User} 10 | 11 | def create?(%User{id: user_id}, %{"user_id" => author_id}) 12 | when user_id == author_id and not is_nil(user_id), do: true 13 | def create?(%User{}, %{}), do: false 14 | 15 | def update?(%User{} = user, %Task{} = task) do 16 | case task |> task_authored_by?(user) do 17 | true -> true 18 | false -> task |> get_project |> administered_by?(user) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/code_corps/policy/user.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.User do 2 | @moduledoc ~S""" 3 | Contains authorization policies for performing actions on a `User` record. 4 | 5 | Used to authorize controller actions. 6 | """ 7 | alias CodeCorps.User 8 | 9 | @spec update?(User.t, User.t) :: boolean 10 | def update?(%User{id: current_user_id}, %User{id: user_id}) 11 | when current_user_id == user_id, do: true 12 | def update?(%User{}, %User{}), do: false 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps/policy/user_category.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.UserCategory do 2 | alias CodeCorps.UserCategory 3 | alias CodeCorps.User 4 | 5 | def create?(%User{admin: true}, %UserCategory{}), do: true 6 | def create?(%User{id: id}, %{"user_id" => user_id}), do: id == user_id 7 | def create?(%User{}, %{}), do: false 8 | 9 | def delete?(%User{admin: true}, %UserCategory{}), do: true 10 | def delete?(%User{id: id}, %UserCategory{user_id: user_id}), do: id == user_id 11 | def delete?(%User{}, %{}), do: false 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps/policy/user_role.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.UserRole do 2 | alias CodeCorps.UserRole 3 | alias CodeCorps.User 4 | 5 | def create?(%User{admin: true}, %{}), do: true 6 | def create?(%User{id: id}, %{"user_id" => user_id}), do: id == user_id 7 | def create?(%User{}, %{}), do: false 8 | 9 | def delete?(%User{admin: true}, %UserRole{}), do: true 10 | def delete?(%User{id: id}, %UserRole{user_id: user_id}), do: id == user_id 11 | def delete?(%User{}, %UserRole{}), do: false 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps/policy/user_skill.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.UserSkill do 2 | alias CodeCorps.{UserSkill, User} 3 | 4 | def create?(%User{admin: true}, %{}), do: true 5 | def create?(%User{id: id}, %{"user_id" => user_id}), do: id == user_id 6 | def create?(%User{}, %{}), do: false 7 | 8 | def delete?(%User{admin: true}, %UserSkill{}), do: true 9 | def delete?(%User{id: id}, %UserSkill{user_id: user_id}), do: id == user_id 10 | def delete?(%User{}, %UserSkill{}), do: false 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps/processor/async.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Processor.Async do 2 | @behaviour CodeCorps.Processor 3 | 4 | @spec process((() -> any)) :: {:ok, pid} 5 | def process(fun) do 6 | Task.Supervisor.start_child(:background_processor, fn -> 7 | apply(fun, []) 8 | end) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps/processor/processor.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Processor do 2 | @processor Application.get_env(:code_corps, :processor) 3 | 4 | @type result :: {:ok, pid} | any 5 | 6 | @callback process(fun :: (() -> any)) :: result 7 | 8 | @spec process((() -> any)) :: result 9 | def process(fun) do 10 | @processor.process(fun) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps/processor/sync.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Processor.Sync do 2 | @behaviour CodeCorps.Processor 3 | 4 | @spec process((() -> any)) :: any 5 | def process(fun) do 6 | apply(fun, []) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps/random_icon_color/generator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RandomIconColor.Generator do 2 | def generate do 3 | ~w(blue green light_blue pink purple yellow) |> Enum.random 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/random_icon_color/test_generator.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RandomIconColor.TestGenerator do 2 | def generate do 3 | "blue" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo do 2 | use Ecto.Repo, otp_app: :code_corps 3 | use Scrivener, page_size: 10 4 | 5 | @dialyzer {:nowarn_function, rollback: 1} 6 | end 7 | -------------------------------------------------------------------------------- /lib/code_corps/sentry/async.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Sentry.Async do 2 | def capture_exception(exception, opts \\ []) do 3 | exception 4 | |> Sentry.capture_exception(opts) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/code_corps/sentry/sentry.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Sentry do 2 | @sentry Application.get_env(:code_corps, :sentry) 3 | 4 | def capture_exception(exception, opts \\ []) do 5 | @sentry.capture_exception(exception, opts) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/sentry/sync.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Sentry.Sync do 2 | def capture_exception(exception, opts \\ []) do 3 | exception 4 | |> Sentry.capture_exception(opts |> Keyword.put(:result, :sync)) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/code_corps/services/forgot_password.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Services.ForgotPasswordService do 2 | # credo:disable-for-this-file Credo.Check.Refactor.PipeChainStart 3 | 4 | alias CodeCorps.{AuthToken, Emails, Mailer, Repo, User} 5 | 6 | @doc""" 7 | Generates an AuthToken model and sends to the provided email. 8 | """ 9 | def forgot_password(email) do 10 | with %User{} = user <- Repo.get_by(User, email: email), 11 | { :ok, %AuthToken{} = %{ value: token } } <- AuthToken.changeset(%AuthToken{}, user) |> Repo.insert 12 | do 13 | Emails.ForgotPasswordEmail.create(user, token) |> Mailer.deliver_now() 14 | else 15 | nil -> nil 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps/services/project_service.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Services.ProjectService do 2 | @moduledoc """ 3 | Handles special CRUD operations for `CodeCorps.Project`. 4 | """ 5 | 6 | import Ecto.Query 7 | 8 | alias CodeCorps.{Project, Repo, StripeConnectPlan, StripeConnectSubscription} 9 | 10 | def update_project_totals(%Project{stripe_connect_plan: %StripeConnectPlan{id: plan_id}} = project) do 11 | total_monthly_donated = 12 | StripeConnectSubscription 13 | |> where([s], s.status == "active" and s.stripe_connect_plan_id == ^plan_id) 14 | |> Repo.aggregate(:sum, :quantity) 15 | |> default_to_zero 16 | 17 | project 18 | |> Project.update_total_changeset(%{total_monthly_donated: total_monthly_donated}) 19 | |> Repo.update 20 | end 21 | 22 | defp default_to_zero(nil), do: 0 23 | defp default_to_zero(value), do: value 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/account_updated.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.AccountUpdated do 2 | def handle(%{data: %{object: %{id: id_from_stripe}}}) do 3 | CodeCorps.StripeService.StripeConnectAccountService.update_from_stripe(id_from_stripe) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/connect_external_account_created.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.ConnectExternalAccountCreated do 2 | def handle(%{data: %{object: %{account: account_id_from_stripe, id: id_from_stripe}}}) do 3 | CodeCorps.StripeService.StripeConnectExternalAccountService.create(id_from_stripe, account_id_from_stripe) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/customer_source_updated.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.CustomerSourceUpdated do 2 | def handle(%{data: %{object: %Stripe.Card{id: card_id}}}) do 3 | CodeCorps.StripeService.StripePlatformCardService.update_from_stripe(card_id) 4 | end 5 | 6 | def handle(_data), do: {:error, :unsupported_object} 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/customer_subscription_deleted.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.CustomerSubscriptionDeleted do 2 | def handle(%{data: %{object: %{id: stripe_sub_id, customer: connect_customer_id}}}) do 3 | CodeCorps.StripeService.StripeConnectSubscriptionService.update_from_stripe(stripe_sub_id, connect_customer_id) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/customer_subscription_updated.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.CustomerSubscriptionUpdated do 2 | def handle(%{data: %{object: %{id: stripe_sub_id, customer: connect_customer_id}}}) do 3 | CodeCorps.StripeService.StripeConnectSubscriptionService.update_from_stripe(stripe_sub_id, connect_customer_id) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/customer_updated.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.CustomerUpdated do 2 | def handle(%{data: %{object: %{id: id_from_stripe}}}) do 3 | CodeCorps.StripeService.StripePlatformCustomerService.update_from_stripe(id_from_stripe) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/events/invoice_payment_succeeded.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.Events.InvoicePaymentSucceeded do 2 | def handle(%{data: %{object: 3 | %{id: id_from_stripe, customer: customer_id_from_stripe} 4 | }}) do 5 | CodeCorps.StripeService.StripeInvoiceService.create(id_from_stripe, customer_id_from_stripe) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/stripe_connect_external_account_service.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.StripeConnectExternalAccountService do 2 | @moduledoc """ 3 | Used to perform actions on a `StripeConnectExternalAccount` record while 4 | propagating to and from the associated `Stripe.BankAccount` record. 5 | """ 6 | alias CodeCorps.{Repo, StripeConnectAccount, StripeExternalAccount} 7 | alias CodeCorps.StripeService.Adapters.StripeExternalAccountAdapter 8 | 9 | @spec create(Stripe.BankAccount.t, StripeConnectAccount.t) :: {:ok, StripeExternalAccount.t} 10 | def create(%Stripe.BankAccount{} = external_account, %StripeConnectAccount{} = connect_account) do 11 | with {:ok, params} <- StripeExternalAccountAdapter.to_params(external_account, connect_account) do 12 | %StripeExternalAccount{} |> StripeExternalAccount.changeset(params) |> Repo.insert 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_service/webhook_processing/environment_filter.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.WebhookProcessing.EnvironmentFilter do 2 | @moduledoc """ 3 | Used to filter events based on environment 4 | """ 5 | 6 | @doc """ 7 | Returns true if the livemode attribute of the event matches 8 | the current environment of the Phoenix application. 9 | 10 | - livemode events are processed only in production. 11 | - non-livemode events are processed in all other environments 12 | """ 13 | def environment_matches?(%{"livemode" => livemode}) do 14 | case Application.get_env(:code_corps, :stripe_env) do 15 | :prod -> livemode 16 | _ -> !livemode 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/charge.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.Charge do 2 | import CodeCorps.StripeTesting.Helpers 3 | 4 | def retrieve(id, _opts) do 5 | {:ok, load_fixture(id)} 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/external_account.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.ExternalAccount do 2 | def retrieve(id, _) do 3 | {:ok, bank_account(id)} 4 | end 5 | 6 | defp bank_account(id) do 7 | %Stripe.BankAccount{ 8 | id: id, 9 | object: "bank_account", 10 | account: "acct_1032D82eZvKYlo2C", 11 | account_holder_name: "Jane Austen", 12 | account_holder_type: "individual", 13 | bank_name: "STRIPE TEST BANK", 14 | country: "US", 15 | currency: "usd", 16 | default_for_currency: false, 17 | fingerprint: "1JWtPxqbdX5Gamtc", 18 | last4: "6789", 19 | metadata: {}, 20 | routing_number: "110000000", 21 | status: "new" 22 | } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/fixtures/account.json: -------------------------------------------------------------------------------- 1 | { 2 | "business_name": "Code Corps PBC", 3 | "business_primary_color": null, 4 | "business_url": "codecorps.org", 5 | "charges_enabled": true, 6 | "country": "US", 7 | "default_currency": "usd", 8 | "details_submitted": true, 9 | "display_name": "Code Corps Customer", 10 | "email": "volunteers@codecorps.org", 11 | "external_accounts": { 12 | "object": "list", 13 | "data": [], 14 | "has_more": false, 15 | "total_count": 0, 16 | "url": "/v1/accounts/acct_123/external_accounts" 17 | }, 18 | "id": "acct_123", 19 | "object": "account", 20 | "metadata": {}, 21 | "statement_descriptor": "CODECORPS.ORG", 22 | "support_email": null, 23 | "support_phone": "1234567890", 24 | "support_url": null, 25 | "timezone": "America/Los_Angeles", 26 | "type": "custom", 27 | "payouts_enabled": true 28 | } 29 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.Helpers do 2 | @moduledoc """ 3 | Used to load JSON fitures which simulate Stripe API responses into 4 | stripity_stripe structs 5 | """ 6 | @fixture_path "./lib/code_corps/stripe_testing/fixtures/" 7 | 8 | @doc """ 9 | Load a stripe response fixture through stripity_stripe, into a 10 | stripity_stripe struct 11 | """ 12 | @spec load_fixture(String.t) :: struct 13 | def load_fixture(id) do 14 | id 15 | |> load_raw_fixture() 16 | |> Stripe.Converter.convert_result 17 | end 18 | 19 | @spec load_raw_fixture(String.t) :: map 20 | def load_raw_fixture(id) do 21 | id 22 | |> build_file_path 23 | |> File.read! 24 | |> Poison.decode! 25 | end 26 | 27 | defp build_file_path(id), do: id |> append_extension |> join_with_path 28 | defp append_extension(id), do: id <> ".json" 29 | defp join_with_path(filename), do: @fixture_path <> filename 30 | end 31 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/invoice.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.Invoice do 2 | import CodeCorps.StripeTesting.Helpers 3 | 4 | def retrieve(id, _opts) do 5 | {:ok, load_fixture(id)} 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/plan.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.Plan do 2 | def create(map, _opts \\ []) do 3 | {:ok, do_create(map)} 4 | end 5 | 6 | defp do_create(_) do 7 | %Stripe.Plan{ 8 | id: "plan_9aMOFmqy1esIRE", 9 | amount: 5000, 10 | created: 1_479_472_835, 11 | currency: "usd", 12 | interval: "month", 13 | interval_count: 1, 14 | livemode: false, 15 | metadata: %{}, 16 | name: "Monthly subscription for Code Corps", 17 | statement_descriptor: nil, 18 | trial_period_days: nil 19 | } 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/code_corps/stripe_testing/token.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeTesting.Token do 2 | def create(_params, _opts = [connect_account: _]) do 3 | {:ok, do_create()} 4 | end 5 | 6 | defp do_create() do 7 | %Stripe.Token{ 8 | id: "sub_123" 9 | } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/github_issue_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubIssueController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.{GithubIssue, Helpers.Query} 6 | 7 | action_fallback CodeCorpsWeb.FallbackController 8 | plug CodeCorpsWeb.Plug.DataToAttributes 9 | plug CodeCorpsWeb.Plug.IdsToIntegers 10 | 11 | @spec index(Conn.t, map) :: Conn.t 12 | def index(%Conn{} = conn, %{} = params) do 13 | with github_repos <- GithubIssue |> Query.id_filter(params) |> Repo.all do 14 | conn |> render("index.json-api", data: github_repos) 15 | end 16 | end 17 | 18 | @spec show(Conn.t, map) :: Conn.t 19 | def show(%Conn{} = conn, %{"id" => id}) do 20 | with %GithubIssue{} = github_repo <- GithubIssue |> Repo.get(id) do 21 | conn |> render("show.json-api", data: github_repo) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/github_pull_request_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubPullRequestController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.{GithubPullRequest, Helpers.Query} 6 | 7 | action_fallback CodeCorpsWeb.FallbackController 8 | plug CodeCorpsWeb.Plug.DataToAttributes 9 | plug CodeCorpsWeb.Plug.IdsToIntegers 10 | 11 | @spec index(Conn.t, map) :: Conn.t 12 | def index(%Conn{} = conn, %{} = params) do 13 | with github_pull_requests <- GithubPullRequest |> Query.id_filter(params) |> Repo.all do 14 | conn |> render("index.json-api", data: github_pull_requests) 15 | end 16 | end 17 | 18 | @spec show(Conn.t, map) :: Conn.t 19 | def show(%Conn{} = conn, %{"id" => id}) do 20 | with %GithubPullRequest{} = github_pull_request <- GithubPullRequest |> Repo.get(id) do 21 | conn |> render("show.json-api", data: github_pull_request) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/page_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PageController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | def index(conn, _params) do 6 | redirect conn, external: "http://docs.codecorpsapi.apiary.io/" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/password_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PasswordController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.{Services.ForgotPasswordService} 6 | 7 | @doc """ 8 | Generates a `CodeCorps.AuthToken` model to verify against and sends an email. 9 | """ 10 | def forgot_password(conn, %{"email" => email}) do 11 | ForgotPasswordService.forgot_password(email) 12 | 13 | conn 14 | |> CodeCorps.Guardian.Plug.sign_out() 15 | |> put_status(:ok) 16 | |> render("show.json", email: email) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/preview_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PreviewController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.{Preview, User} 6 | 7 | action_fallback CodeCorpsWeb.FallbackController 8 | plug CodeCorpsWeb.Plug.DataToAttributes 9 | plug CodeCorpsWeb.Plug.IdsToIntegers 10 | 11 | @spec create(Plug.Conn.t, map) :: Conn.t 12 | def create(%Conn{} = conn, %{} = params) do 13 | with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource, 14 | {:ok, :authorized} <- current_user |> Policy.authorize(:create, %Preview{}, params), 15 | {:ok, %Preview{} = preview} <- %Preview{} |> Preview.create_changeset(params) |> Repo.insert do 16 | conn |> put_status(:created) |> render("show.json-api", data: preview) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/slugged_route_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.SluggedRouteController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.{SluggedRoute, Helpers.Query} 6 | 7 | action_fallback CodeCorpsWeb.FallbackController 8 | 9 | @spec show(Conn.t, map) :: Conn.t 10 | def show(%Conn{} = conn, %{"slug" => slug}) do 11 | with %SluggedRoute{} = slugged_route <- SluggedRoute |> Query.slug_finder(slug) do 12 | conn |> render("show.json-api", data: slugged_route) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/stripe_connect_events_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.StripeConnectEventsController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.StripeService.WebhookProcessing.{ 6 | ConnectEventHandler, EnvironmentFilter, WebhookProcessor 7 | } 8 | 9 | def create(conn, event_params) do 10 | case EnvironmentFilter.environment_matches?(event_params) do 11 | true -> 12 | {:ok, _pid} = WebhookProcessor.process_async(event_params, ConnectEventHandler) 13 | conn |> send_resp(200, "") 14 | false -> 15 | conn |> send_resp(400, "") 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps_web/controllers/stripe_platform_events_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.StripePlatformEventsController do 2 | @moduledoc false 3 | use CodeCorpsWeb, :controller 4 | 5 | alias CodeCorps.StripeService.WebhookProcessing.{ 6 | EnvironmentFilter, PlatformEventHandler, WebhookProcessor 7 | } 8 | 9 | def create(conn, event_params) do 10 | case EnvironmentFilter.environment_matches?(event_params) do 11 | true -> 12 | {:ok, _pid} = WebhookProcessor.process_async(event_params, PlatformEventHandler) 13 | conn |> send_resp(200, "") 14 | false -> 15 | conn |> send_resp(400, "") 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps_web/plugs/analytics_identify.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.AnalyticsIdentify do 2 | @moduledoc """ 3 | Plug used to identify the current user on Segment.com using `CodeCorps.Analytics.Segment`. 4 | """ 5 | 6 | def init(opts), do: opts 7 | 8 | def call(conn, _opts), do: conn |> identify 9 | 10 | defp identify(%{assigns: %{current_user: user}} = conn) do 11 | CodeCorps.Analytics.SegmentTracker.identify(user) 12 | conn 13 | end 14 | defp identify(conn), do: conn 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps_web/plugs/current_user.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.CurrentUser do 2 | @moduledoc """ 3 | Puts authenticated Guardian user into conn.assigns[:current_user] 4 | """ 5 | 6 | @spec init(Keyword.t) :: Keyword.t 7 | def init(opts), do: opts 8 | 9 | @spec call(Plug.Conn.t, Keyword.t) :: Plug.Conn.t 10 | def call(conn, _opts) do 11 | case Guardian.Plug.current_resource(conn) do 12 | user = %CodeCorps.User{} -> 13 | Plug.Conn.assign(conn, :current_user, user) 14 | nil -> 15 | conn 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps_web/plugs/segment.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.Segment do 2 | @moduledoc """ 3 | Used for reporting segment events 4 | """ 5 | 6 | import Plug.Conn, only: [register_before_send: 2] 7 | 8 | @spec init(Keyword.t) :: Keyword.t 9 | def init(opts), do: opts 10 | 11 | @spec call(Plug.Conn.t, Keyword.t) :: Plug.Conn.t 12 | def call(conn, _opts) do 13 | register_before_send(conn, &CodeCorps.Analytics.SegmentPlugTracker.maybe_track(&1)) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/code_corps_web/plugs/set_sentry_user_context.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.SetSentryUserContext do 2 | def init(opts), do: opts 3 | 4 | def call(conn, _opts), do: conn |> set_context 5 | 6 | defp set_context(%{assigns: %{current_user: user}} = conn) do 7 | Sentry.Context.set_user_context(%{ 8 | id: user.id, 9 | email: user.email, 10 | first_name: user.first_name, 11 | last_name: user.last_name 12 | }) 13 | conn 14 | end 15 | 16 | defp set_context(conn), do: conn 17 | end 18 | -------------------------------------------------------------------------------- /lib/code_corps_web/plugs/set_timber_user_context.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.SetTimberUserContext do 2 | @moduledoc """ 3 | Captures user context. 4 | """ 5 | 6 | @behaviour Plug 7 | 8 | alias CodeCorps.User 9 | 10 | @impl true 11 | def init(opts), do: opts 12 | 13 | @impl true 14 | def call(%{assigns: %{current_user: user}} = conn, _), do: add_context(conn, user) 15 | def call(conn, _), do: conn 16 | 17 | @impl false 18 | def add_context(conn, %User{} = user) do 19 | %Timber.Contexts.UserContext{ 20 | id: user.id, email: user.email, name: User.full_name(user) 21 | } |> Timber.add_context() 22 | 23 | conn 24 | end 25 | def add_context(conn, _), do: conn 26 | end 27 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/category_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.CategoryView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:name, :slug, :description] 7 | 8 | has_many :project_categories, serializer: CodeCorpsWeb.ProjectCategoryView, identifiers: :always 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/comment_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.CommentView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [ 7 | :body, :created_at, :created_from, :inserted_at, :markdown, :modified_at, 8 | :modified_from, :updated_at 9 | ] 10 | 11 | has_one :task, type: "task", field: :task_id 12 | has_one :user, type: "user", field: :user_id 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/conversation_part_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ConversationPartView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:body, :inserted_at, :read_at, :updated_at] 7 | 8 | has_one :author, type: "user", field: :author_id 9 | has_one :conversation, type: "conversation", field: :conversation_id 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/conversation_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ConversationView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:read_at, :status, :inserted_at, :updated_at] 7 | 8 | has_one :user, type: "user", field: :user_id 9 | has_one :message, type: "message", field: :message_id 10 | 11 | has_many :conversation_parts, serializer: CodeCorpsWeb.ConversationPartView, identifiers: :always 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/donation_goal_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.DonationGoalView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:achieved, :amount, :current, :description] 7 | 8 | has_one :project, type: "project", field: :project_id 9 | 10 | @doc """ 11 | Determines whether the goal has been met by checking the amount against 12 | the project's total monthly donated amount. 13 | """ 14 | def achieved(donation_goal, _conn) do 15 | donation_goal.amount <= donation_goal.project.total_monthly_donated 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/github_app_installation_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubAppInstallationView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes ~w(github_id github_account_id github_account_avatar_url github_account_login github_account_type inserted_at installed state updated_at)a 7 | 8 | has_one :project, type: "project", field: :project_id 9 | has_one :user, type: "user", field: :user_id 10 | 11 | has_many :github_repos, serializer: CodeCorpsWeb.GithubRepoView, identifiers: :always 12 | has_many :organization_github_app_installations, serializer: CodeCorpsWeb.OrganizationGithubAppInstallationView, identifiers: :always 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/github_event_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubEventView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [ 7 | :action, :event_type, :error, :failure_reason, :github_delivery_id, 8 | :inserted_at, :payload, :record_data, :status, :updated_at 9 | ] 10 | 11 | def event_type(github_event, _conn) do 12 | github_event.type 13 | end 14 | 15 | def record_data(github_event, _conn) do 16 | github_event.data 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/github_issue_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubIssueView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [ 7 | :body, :closed_at, :comments_url, :events_url, :github_created_at, 8 | :github_id, :github_updated_at, :html_url, :labels_url, :locked, :number, 9 | :state, :title, :url 10 | ] 11 | 12 | has_one :github_pull_request, type: "github-pull-request", field: :github_pull_request_id 13 | has_one :github_repo, type: "github-repo", field: :github_repo_id 14 | end 15 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/github_pull_request_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubPullRequestView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:github_created_at, :github_updated_at, :html_url, :merged, :number, :state] 7 | 8 | has_one :github_repo, type: "github-repo", field: :github_repo_id 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/github_repo_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.GithubRepoView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:github_account_avatar_url, :github_account_id, 7 | :github_account_login, :github_account_type, :github_id, :inserted_at, 8 | :name, :syncing_comments_count, :syncing_issues_count, 9 | :syncing_pull_requests_count, :sync_state, :updated_at] 10 | 11 | has_one :github_app_installation, type: "github-app-installation", field: :github_app_installation_id 12 | has_one :project, type: "project", field: :project_id 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/layout_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.LayoutView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | end 5 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/message_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.MessageView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:body, :initiated_by, :inserted_at, :subject, :updated_at] 7 | 8 | has_one :author, type: "user", field: :author_id 9 | has_one :project, type: "project", field: :project_id 10 | 11 | has_many :conversations, serializer: CodeCorpsWeb.ConversationView, identifiers: :always 12 | end 13 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/organization_github_app_installation_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.OrganizationGithubAppInstallationView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:inserted_at, :updated_at] 7 | 8 | has_one :github_app_installation, type: "github-app-installation", field: :github_app_installation_id 9 | has_one :organization, type: "organization", field: :organization_id 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/organization_invite_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.OrganizationInviteView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [ 7 | :email, :inserted_at, :organization_name, :updated_at 8 | ] 9 | 10 | has_one :organization, type: "organization", field: :organization_id 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/page_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PageView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | @dialyzer :no_match 5 | end 6 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/password_reset_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PasswordResetView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | 5 | def render("show.json", %{email: email, token: token, user_id: user_id}) do 6 | %{ 7 | email: email, 8 | token: token, 9 | user_id: user_id 10 | } 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/password_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PasswordView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | 5 | def render("show.json", %{email: email}) do 6 | %{ 7 | email: email 8 | } 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/preview_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PreviewView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:markdown, :body, :inserted_at, :updated_at] 7 | 8 | has_one :user, type: "user", field: :user_id 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/project_category_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ProjectCategoryView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :project, type: "project", field: :project_id 7 | has_one :category, type: "category", field: :category_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/project_skill_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ProjectSkillView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :project, type: "project", field: :project_id 7 | has_one :skill, type: "skill", field: :skill_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/project_user_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ProjectUserView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:role, :inserted_at, :updated_at] 7 | 8 | has_one :project, type: "project", field: :project_id 9 | has_one :user, type: "user", field: :user_id 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/role_skill_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.RoleSkillView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :role, type: "role", field: :role_id 7 | has_one :skill, type: "skill", field: :skill_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/role_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.RoleView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:name, :ability, :kind, :inserted_at, :updated_at] 7 | 8 | has_many :role_skills, serializer: CodeCorpsWeb.RoleSkillView, identifiers: :always 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/skill_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.SkillView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:title, :description, :inserted_at, :updated_at] 7 | 8 | has_many :role_skills, serializer: CodeCorpsWeb.RoleSkillView, identifiers: :always 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/slugged_route_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.SluggedRouteView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:slug, :inserted_at, :updated_at] 7 | 8 | has_one :organization, type: "organization", field: :organization_id 9 | has_one :user, type: "user", field: :user_id 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/stripe_connect_plan_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.StripeConnectPlanView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:amount, :created, :id_from_stripe, :inserted_at, :name, :updated_at] 7 | 8 | has_one :project, type: "project", field: :project_id 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/stripe_connect_subscription_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.StripeConnectSubscriptionView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:quantity, :inserted_at, :updated_at] 7 | 8 | has_one :user, type: "user", field: :user_id 9 | has_one :project, serializer: CodeCorpsWeb.ProjectView, through: [:stripe_connect_plan, :project] 10 | end 11 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/stripe_platform_card_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.StripePlatformCardView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:brand, :exp_month, :exp_year, :token, :last4, :name] 7 | 8 | has_one :user, type: "user", field: :user_id 9 | end 10 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/task_list_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.TaskListView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | attributes [:done, :inbox, :name, :order, :pull_requests, :inserted_at, :updated_at] 7 | 8 | has_one :project, type: "project", field: :project_id 9 | 10 | has_many :tasks, serializer: CodeCorpsWeb.TaskView, identifiers: :always 11 | end 12 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/task_skill_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.TaskSkillView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :task, type: "task", field: :task_id 7 | has_one :skill, type: "skill", field: :skill_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/token_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.TokenView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | 5 | def render("show.json", %{token: token, user_id: user_id}) do 6 | %{ 7 | token: token, 8 | user_id: user_id, 9 | } 10 | end 11 | 12 | def render("401.json", %{message: message}) do 13 | %{ 14 | errors: [ 15 | %{ 16 | id: "UNAUTHORIZED", 17 | title: "401 Unauthorized", 18 | detail: message, 19 | status: 401, 20 | } 21 | ] 22 | } 23 | end 24 | 25 | def render("403.json", %{message: message}) do 26 | %{ 27 | errors: [ 28 | %{ 29 | id: "FORBIDDEN", 30 | title: "403 Forbidden", 31 | detail: message, 32 | status: 403, 33 | } 34 | ] 35 | } 36 | end 37 | 38 | def render("delete.json", _) do 39 | %{ok: true} 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/user_category_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.UserCategoryView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :user, type: "user", field: :user_id 7 | has_one :category, type: "category", field: :category_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/user_role_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.UserRoleView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :user, type: "user", field: :user_id 7 | has_one :role, type: "role", field: :role_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/user_skill_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.UserSkillView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :user, type: "user", field: :user_id 7 | has_one :skill, type: "skill", field: :skill_id 8 | end 9 | -------------------------------------------------------------------------------- /lib/code_corps_web/views/user_task_view.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.UserTaskView do 2 | @moduledoc false 3 | use CodeCorpsWeb, :view 4 | use JaSerializer.PhoenixView 5 | 6 | has_one :task, type: "task", field: :task_id 7 | has_one :user, type: "user", field: :user_id 8 | end 9 | -------------------------------------------------------------------------------- /priv/gettext/default.pot: -------------------------------------------------------------------------------- 1 | ## This file is a PO Template file. 2 | ## 3 | ## `msgid`s here are often extracted from source code. 4 | ## Add new translations manually only if they're dynamic 5 | ## translations that can't be statically extracted. 6 | ## 7 | ## Run `mix gettext.extract` to bring this file up to 8 | ## date. Leave `msgstr`s empty as changing them here as no 9 | ## effect: edit them in PO (`.po`) files instead. 10 | msgid "" 11 | msgstr "" 12 | -------------------------------------------------------------------------------- /priv/gettext/en/LC_MESSAGES/default.po: -------------------------------------------------------------------------------- 1 | ## `msgid`s in this file come from POT (.pot) files. 2 | ## 3 | ## Do not add, change, or remove `msgid`s manually here as 4 | ## they're tied to the ones in the corresponding POT file 5 | ## (with the same domain). 6 | ## 7 | ## Use `mix gettext.extract --merge` or `mix gettext.merge` 8 | ## to merge POT files into PO files. 9 | msgid "" 10 | msgstr "" 11 | "Language: en\n" 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160723215749_create_user.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateUser do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:users) do 6 | add :username, :string, null: false 7 | add :email, :string, null: false 8 | add :encrypted_password, :string 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:users, ["lower(username)"], name: :users_lower_username_index, unique: true) 14 | create index(:users, [:email], unique: true) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160804000000_create_organization.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateOrganization do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:organizations) do 6 | add :name, :string 7 | add :description, :string 8 | add :slug, :string 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:organizations, ["lower(slug)"], name: :organizations_lower_slug_index, unique: true) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160804001111_create_slugged_route.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateSluggedRoute do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:slugged_routes) do 6 | add :slug, :string 7 | add :organization_id, references(:organizations, on_delete: :nothing) 8 | add :user_id, references(:users, on_delete: :nothing) 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:slugged_routes, ["lower(slug)"], name: :slugged_routes_lower_slug_index, unique: true) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160805132301_add_user_profile_fields.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUserProfileFields do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :first_name, :string 7 | add :last_name, :string 8 | add :website, :string 9 | add :twitter, :string 10 | add :biography, :string 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160805203929_create_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateSkill do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:skills) do 6 | add :title, :string, null: false 7 | add :description, :string 8 | add :original_row, :integer 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:skills, ["lower(title)"], name: :index_skills_on_title, unique: true) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160808143454_create_category.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateCategory do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:categories) do 6 | add :name, :string, null: false 7 | add :slug, :string, null: false 8 | add :description, :string 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:categories, ["lower(slug)"], unique: true, name: :index_categories_on_slug) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160809214736_create_role.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateRole do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:roles) do 6 | add :name, :string, null: false 7 | add :ability, :string, null: false 8 | add :kind, :string, null: false 9 | 10 | timestamps() 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160810124357_create_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:projects) do 6 | add :description, :string 7 | add :icon_large_url, :string 8 | add :icon_thumb_url, :string 9 | add :long_description_body, :string 10 | add :long_description_markdown, :string 11 | add :slug, :string, null: false 12 | add :title, :string, null: false 13 | 14 | add :organization_id, references(:organizations, on_delete: :nothing) 15 | 16 | timestamps() 17 | end 18 | 19 | create index(:projects, ["lower(slug)"], unique: true, name: :index_projects_on_slug) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160815125009_create_preview.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreatePreview do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:previews) do 6 | add :markdown, :text, null: false 7 | add :body, :text, null: false 8 | 9 | add :user_id, references(:users, on_delete: :nothing) 10 | 11 | timestamps() 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160815143002_create_user_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateUserSkill do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:user_skills) do 6 | add :user_id, references(:users, on_delete: :nothing) 7 | add :skill_id, references(:skills, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:user_skills, [:user_id, :skill_id], unique: true, name: :index_projects_on_user_id_skill_id) 13 | end 14 | end 15 | 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160816020347_create_post.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreatePost do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:posts) do 6 | add :body, :string 7 | add :markdown, :string 8 | add :number, :integer, null: false 9 | add :post_type, :string, null: false, default: "task" 10 | add :state, :string 11 | add :status, :string, null: false, default: "open" 12 | add :title, :string 13 | 14 | add :project_id, references(:projects, on_delete: :nothing), null: false 15 | add :user_id, references(:users, on_delete: :nothing), null: false 16 | 17 | timestamps() 18 | end 19 | 20 | create index(:posts, [:project_id]) 21 | create index(:posts, [:user_id]) 22 | create index(:posts, [:number, :project_id], unique: true) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160816034021_create_role_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateRoleSkill do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:role_skills) do 6 | add :role_id, references(:roles, on_delete: :nothing) 7 | add :skill_id, references(:skills, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:role_skills, [:role_id, :skill_id], unique: true, name: :index_projects_on_role_id_skill_id) 13 | 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160817220118_create_comment.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateComment do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:comments) do 6 | add :body, :text, null: false 7 | add :markdown, :text, null: false 8 | add :user_id, references(:users, on_delete: :nothing) 9 | add :post_id, references(:posts, on_delete: :nothing) 10 | 11 | timestamps() 12 | end 13 | 14 | create index(:comments, [:user_id]) 15 | create index(:comments, [:post_id]) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160818132546_add_organization_membership.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateOrganizationMembership do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:organization_memberships) do 6 | add :role, :string, null: false 7 | add :organization_id, references(:organizations, on_delete: :nothing), null: false 8 | add :member_id, references(:users, on_delete: :nothing), null: false 9 | 10 | timestamps() 11 | end 12 | 13 | create index :organization_memberships, [:member_id, :organization_id], unique: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160820113856_add_project_category.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateProjectCategory do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:project_categories) do 6 | add :project_id, references(:projects, on_delete: :nothing), null: false 7 | add :category_id, references(:categories, on_delete: :nothing), null: false 8 | 9 | timestamps() 10 | end 11 | 12 | create index :project_categories, [:project_id, :category_id], unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160820164905_add_photo_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddPhotoToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :photo, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160822002438_add_icon_to_projects.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddIconToProjects do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :icon, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160822004056_add_icon_to_organizations.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddIconToOrganizations do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | add :icon, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160822011624_create_user_role.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateUserRole do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:user_roles) do 6 | add :user_id, references(:users, on_delete: :nothing) 7 | add :role_id, references(:roles, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:user_roles, [:user_id, :role_id], unique: true) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160822020401_create_user_category.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateUserCategory do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:user_categories) do 6 | add :user_id, references(:users, on_delete: :nothing) 7 | add :category_id, references(:categories, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:user_categories, [:user_id, :category_id], unique: true) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160822044612_create_project_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateProjectSkill do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:project_skills) do 6 | add :project_id, references(:projects, on_delete: :nothing) 7 | add :skill_id, references(:skills, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:project_skills, [:project_id]) 13 | create index(:project_skills, [:skill_id]) 14 | create index(:project_skills, [:project_id, :skill_id], unique: true) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160830081224_add_admin_to_user.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddAdminToUser do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :admin, :boolean, null: false, default: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160830224802_add_state_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddStateToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :state, :string, default: "signed_up" 7 | end 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160911233738_remove_icon_urls_from_projects.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveIconUrlsFromProjects do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | remove :icon_large_url 7 | remove :icon_thumb_url 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160912145957_add_cat_to_role_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCatToRoleSkill do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:role_skills) do 6 | add :cat, :integer 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20160918003206_fix_mixed_case_slugs.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.FixMixedCaseSlugs do 2 | use Ecto.Migration 3 | 4 | alias CodeCorps.Repo 5 | alias CodeCorps.SluggedRoute 6 | 7 | def up do 8 | SluggedRoute 9 | |> Repo.all 10 | |> Repo.preload([:user, :organization]) 11 | |> Enum.each(fn record -> 12 | SluggedRoute.changeset(record) 13 | |> Ecto.Changeset.put_change(:slug, Inflex.parameterize(record.slug)) 14 | |> Repo.update! 15 | end) 16 | end 17 | 18 | def down do 19 | # Nothing to do here 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161019110737_create_donation_goal.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateDonationGoal do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:donation_goals) do 6 | add :amount, :integer 7 | add :current, :boolean 8 | add :description, :text 9 | add :project_id, references(:projects) 10 | add :title, :string, null: false 11 | 12 | timestamps() 13 | end 14 | 15 | create index(:donation_goals, [:project_id]) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161031001615_remove_title_from_donation_goals.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveTitleFromDonationGoals do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:donation_goals) do 6 | remove :title 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:donation_goals) do 12 | add :title, :string, null: false 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161121014050_create_stripe_connect_customers.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateStripeConnectCustomers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stripe_connect_customers) do 6 | add :id_from_stripe, :string, null: false 7 | 8 | add :stripe_account_id, references(:stripe_accounts, on_delete: :nothing), null: false 9 | add :stripe_platform_customer_id, references(:stripe_platform_customers, on_delete: :nothing), null: false 10 | 11 | timestamps() 12 | end 13 | 14 | create unique_index(:stripe_connect_customers, [:id_from_stripe]) 15 | create unique_index(:stripe_connect_customers, [:stripe_account_id, :stripe_platform_customer_id]) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161121045709_create_stripe_connect_cards.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateStripeConnectCards do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stripe_connect_cards) do 6 | add :id_from_stripe, :string, null: false 7 | 8 | add :stripe_account_id, references(:stripe_accounts, on_delete: :nothing), null: false 9 | add :stripe_platform_card_id, references(:stripe_platform_cards, on_delete: :nothing), null: false 10 | 11 | timestamps() 12 | end 13 | 14 | create unique_index(:stripe_connect_cards, [:id_from_stripe]) 15 | create unique_index(:stripe_connect_cards, [:stripe_account_id, :stripe_platform_card_id]) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161124085742_switch_from_multiple_to_single_card.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.SwitchFromMultipleToSingleCard do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop_if_exists index(:stripe_platform_cards, [:user_id]) 6 | create unique_index(:stripe_platform_cards, [:user_id]) 7 | end 8 | 9 | def down do 10 | drop_if_exists unique_index(:stripe_platform_cards, [:user_id]) 11 | create index(:stripe_platform_cards, [:user_id]) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161125200620_add_total_donated_to_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddTotalDonatedToProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :total_monthly_donated, :integer, default: 0 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161126045705_add_current_donation_goal.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCurrentDonationGoal do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:projects) do 6 | add :current_donation_goal_id, references(:donation_goals) 7 | end 8 | 9 | create unique_index(:projects, [:current_donation_goal_id]) 10 | 11 | alter table(:donation_goals) do 12 | remove(:current) 13 | end 14 | end 15 | 16 | def down do 17 | drop_if_exists unique_index(:projects, [:current_donation_goal_id]) 18 | 19 | alter table(:projects) do 20 | remove(:current_donation_goal_id) 21 | end 22 | 23 | alter table(:donation_goals) do 24 | add :current, :boolean, default: false 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161127054559_revert_back_to_current_on_donation_goals.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RevertBackToCurrentOnDonationGoals do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop_if_exists unique_index(:projects, [:current_donation_goal_id]) 6 | 7 | alter table(:projects) do 8 | remove(:current_donation_goal_id) 9 | end 10 | 11 | alter table(:donation_goals) do 12 | add :current, :boolean, default: false 13 | end 14 | 15 | execute "CREATE UNIQUE INDEX donation_goals_current_unique_to_project ON donation_goals (project_id) WHERE current" 16 | end 17 | 18 | def down do 19 | execute "DROP INDEX IF EXISTS donation_goals_current_unique" 20 | 21 | alter table(:projects) do 22 | add :current_donation_goal_id, references(:donation_goals) 23 | end 24 | 25 | create unique_index(:projects, [:current_donation_goal_id]) 26 | 27 | alter table(:donation_goals) do 28 | remove(:current) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161205024856_add_approved_to_projects.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddApprovedToProjects do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :approved, :boolean, default: false 7 | end 8 | create index(:projects, [:approved]) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161207112519_add_stripe_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddStripeEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stripe_events) do 6 | add :id_from_stripe, :string, null: false 7 | add :status, :string, default: "unprocessed" 8 | add :type, :string, null: false 9 | 10 | timestamps() 11 | end 12 | 13 | create unique_index(:stripe_events, [:id_from_stripe]) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161209192504_create_task_list.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateTaskList do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:task_lists) do 6 | add :name, :string 7 | add :order, :integer 8 | add :project_id, references(:projects, on_delete: :nothing) 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:task_lists, [:project_id]) 14 | 15 | alter table(:tasks) do 16 | add :task_list_id, references(:task_lists, on_delete: :nothing) 17 | add :order, :integer 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161212005641_create_stripe_file_upload.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateStripeFileUpload do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stripe_file_upload) do 6 | add :created, :utc_datetime 7 | add :id_from_stripe, :string, null: false 8 | add :purpose, :string 9 | add :size, :integer 10 | add :type, :string 11 | add :url, :string 12 | add :stripe_connect_account_id, references(:stripe_connect_accounts) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161214005935_create_stripe_external_account.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateStripeExternalAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stripe_external_accounts) do 6 | add :id_from_stripe, :string, null: false 7 | add :account_id_from_stripe, :string, null: false 8 | add :account_holder_name, :string 9 | add :account_holder_type, :string 10 | add :bank_name, :string 11 | add :country, :string 12 | add :currency, :string 13 | add :default_for_currency, :string 14 | add :fingerprint, :string 15 | add :last4, :string 16 | add :routing_number, :string 17 | add :status, :string 18 | 19 | timestamps() 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161215052051_add_user_id_and_endpoint_to_stripe_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUserIdAndEndpointToStripeEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_events) do 6 | add :endpoint, :string, null: false 7 | add :user_id, :string 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161216051447_add_approved_to_organizations.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddApprovedToOrganizations do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | add :approved, :boolean, default: false 7 | end 8 | 9 | create index(:organizations, :approved) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161218005913_add_verification_fields_to_stripe_connect_account.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddVerificationFieldsToStripeConnectAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | add :verification_disabled_reason, :string 7 | add :verification_due_by, :utc_datetime 8 | add :verification_fields_needed, {:array, :string} 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161219160401_change_external_account_default_for_currency_to_boolean.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeExternalAccountDefaultForCurrencyToBoolean do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_external_accounts) do 6 | remove :default_for_currency 7 | add :default_for_currency, :boolean 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161219163909_add_external_account_to_stripe_connect_accounts.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddExternalAccountToStripeConnectAccounts do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | add :external_account, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161220141753_add_status_fields_to_stripe_connect_accounts.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddStatusFieldsToStripeConnectAccounts do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | add :recipient_status, :string 7 | add :verification_document_status, :string 8 | add :personal_id_number_status, :string 9 | add :bank_account_status, :string 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161226213600_remove_obsolete_status_fields_from_connect_account.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveObsoleteStatusFieldsFromConnectAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | remove :recipient_status 7 | remove :verification_document_status 8 | remove :personal_id_number_status 9 | remove :bank_account_status 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/migrations/20161231063614_alter_dob_fields_to_integer.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AlterDobFieldsToInteger do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | remove :legal_entity_dob_day 7 | remove :legal_entity_dob_month 8 | remove :legal_entity_dob_year 9 | 10 | add :legal_entity_dob_day, :integer 11 | add :legal_entity_dob_month, :integer 12 | add :legal_entity_dob_year, :integer 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170102130055_add_tos_acceptance_fields_to_stripe_connect_accounts.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddTosAcceptanceFieldsToStripeConnectAccounts do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_accounts) do 6 | add :tos_acceptance_date, :utc_datetime 7 | add :tos_acceptance_ip, :string 8 | add :tos_acceptance_user_agent, :string 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170104113708_add_stripe_connect_account_reference_to_external_accounts.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddStripeConnectAccountReferenceToExternalAccounts do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_external_accounts) do 6 | add :stripe_connect_account_id, references(:stripe_connect_accounts) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170104235423_add_user_to_stripe_connect_customer.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUserToStripeConnectCustomer do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_connect_customers) do 6 | add :user_id, references(:users), null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170106013143_add_editable_to_task_lists.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddEditableToTaskLists do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:task_lists) do 6 | add :inbox, :boolean, default: false 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:task_lists) do 12 | remove :inbox 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170115035159_add_object_id_type_to_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddObjectIdTypeToEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_events) do 6 | add :object_id, :string, null: false 7 | add :object_type, :string, null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170115230549_add_ignored_reason_to_stripe_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddIgnoredReasonToStripeEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:stripe_events) do 6 | add :ignored_reason, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170131234029_create_task_skill.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateTaskSkill do 2 | @moduledoc false 3 | 4 | use Ecto.Migration 5 | 6 | def change do 7 | create table(:task_skills) do 8 | add :skill_id, references(:skills), null: false 9 | add :task_id, references(:tasks), null: false 10 | 11 | timestamps() 12 | end 13 | 14 | create index :task_skills, [:task_id, :skill_id], unique: true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170201014901_add_cloudinary_public_ids.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCloudinaryImageIds do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | add :cloudinary_public_id, :string 7 | end 8 | 9 | alter table(:projects) do 10 | add :cloudinary_public_id, :string 11 | end 12 | 13 | alter table(:users) do 14 | add :cloudinary_public_id, :string 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170201025454_add_default_colors.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddDefaultColors do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | add :default_color, :string 7 | end 8 | 9 | alter table(:projects) do 10 | add :default_color, :string 11 | end 12 | 13 | alter table(:users) do 14 | add :default_color, :string 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170201035458_create_user_task.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateUserTask do 2 | @moduledoc false 3 | 4 | use Ecto.Migration 5 | 6 | def change do 7 | create table(:user_tasks) do 8 | add :task_id, references(:tasks), null: false 9 | add :user_id, references(:users), null: false 10 | 11 | timestamps() 12 | end 13 | 14 | create index :user_tasks, [:user_id, :task_id], unique: true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170201183258_remove_photo_icon_columns.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemovePhotoIconColumns do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | remove :icon 7 | end 8 | 9 | alter table(:projects) do 10 | remove :icon 11 | end 12 | 13 | alter table(:users) do 14 | remove :photo 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170220032224_remove_task_type_from_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveTaskTypeFromTasks do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:tasks) do 6 | remove :task_type 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170224233516_add_owner_to_organization.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddOwnerToOrganization do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:organizations) do 6 | add :owner_id, references(:users, on_delete: :nothing) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170226050552_add_owner_to_projects.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddOwnerToProjects do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :owner_id, references(:users, on_delete: :nothing) 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170228085250_create_project_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateProjectUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:project_users) do 6 | add :role, :string, null: false 7 | add :project_id, references(:projects, on_delete: :nothing), null: false 8 | add :user_id, references(:users, on_delete: :nothing), null: false 9 | 10 | timestamps() 11 | end 12 | 13 | create index :project_users, [:user_id, :project_id], unique: true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170308214128_add_website_to_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddWebsiteToProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :website, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170308220713_add_should_link_externally_to_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddShouldLinkExternallyToProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :should_link_externally, :boolean, default: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170308222552_create_auth_token.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateAuthToken do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:auth_token) do 6 | add :value, :string 7 | 8 | add :user_id, references(:users, on_delete: :delete_all) 9 | 10 | timestamps() 11 | end 12 | 13 | create index(:auth_token, [:user_id]) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170313130611_remove_project_owner_id.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveProjectOwnerId do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | remove :owner_id 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170318032449_add_check_constraint_to_project_description_when_project_is_approved.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCheckConstraintToProjectDescriptionWhenProjectIsApproved do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create constraint(:projects, "set_long_description_markdown_if_approved", check: "long_description_markdown IS NOT NULL OR approved = false") 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170318082740_drop_organization_membership.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.DropOrganizationMembership do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop index :organization_memberships, [:member_id, :organization_id] 6 | drop table(:organization_memberships) 7 | end 8 | 9 | def down do 10 | create table(:organization_memberships) do 11 | add :role, :string, null: false 12 | add :organization_id, references(:organizations, on_delete: :nothing), null: false 13 | add :member_id, references(:users, on_delete: :nothing), null: false 14 | 15 | timestamps() 16 | end 17 | 18 | create index :organization_memberships, [:member_id, :organization_id], unique: true 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170324194827_add_sign_up_context_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddSignUpContextToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :sign_up_context, :string, default: "default" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170424215355_add-github-id-to-user.exs: -------------------------------------------------------------------------------- 1 | defmodule :"Elixir.CodeCorps.Repo.Migrations.Add-github-id-to-user" do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :github_id, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170501225441_add_github_ids.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGitHubIds do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:comments) do 6 | add :github_id, :integer 7 | end 8 | 9 | alter table(:tasks) do 10 | add :github_id, :integer 11 | end 12 | 13 | alter table(:projects) do 14 | add :github_id, :integer 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170505224222_add_github_repo_and_owner_to_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGitHubRepoAndOwnerToProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :github_repo, :string 7 | add :github_owner, :string 8 | remove :github_id 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170526095401_add_github_auth_token_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGitHubAuthTokenToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :github_auth_token, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170602000208_add_github_details_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubDetailsToUsers do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:users) do 6 | add :github_avatar_url, :string 7 | add :github_email, :string 8 | add :github_username, :string 9 | 10 | remove :github_id 11 | add :github_id, :integer 12 | end 13 | end 14 | 15 | def down do 16 | alter table(:users) do 17 | remove :github_avatar_url 18 | remove :github_email 19 | remove :github_username 20 | 21 | remove :github_id 22 | add :github_id, :string 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170622205732_create_github_app_installation.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateGithubAppInstallation do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_app_installations) do 6 | add :github_id, :integer 7 | add :installed, :boolean, default: true 8 | add :state, :string 9 | 10 | add :project_id, references(:projects, on_delete: :nothing) 11 | add :user_id, references(:users, on_delete: :nothing) 12 | 13 | timestamps() 14 | end 15 | 16 | create index(:github_app_installations, [:project_id]) 17 | create index(:github_app_installations, [:user_id]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170626231059_create_github_event.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateGithubEvent do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_events) do 6 | add :action, :string 7 | add :github_delivery_id, :string 8 | add :status, :string 9 | add :source, :string 10 | add :type, :string 11 | 12 | timestamps() 13 | end 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170628092119_add_github_repos.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubRepos do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_repos) do 6 | add :github_id, :integer 7 | add :name, :string 8 | add :github_account_id, :integer 9 | add :github_account_login, :string 10 | add :github_account_avatar_url, :string 11 | add :github_account_type, :string 12 | 13 | add :github_app_installation_id, references(:github_app_installations, on_delete: :nothing) 14 | 15 | timestamps() 16 | end 17 | 18 | create index(:github_repos, [:github_app_installation_id]) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170628213609_add_access_tokens_to_github_app_installations.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddAccessTokensToGithubAppInstallations do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_app_installations) do 6 | add :access_token, :string 7 | add :access_token_expires_at, :utc_datetime 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170629183404_add_unique_index_on_github_id_to_github_app_installations.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUniqueIndexOnGithubIdToGithubAppInstallations do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create unique_index(:github_app_installations, :github_id) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170630140136_create_project_github_repo.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateProjectGithubRepo do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:project_github_repos) do 6 | add :project_id, references(:projects, on_delete: :delete_all) 7 | add :github_repo_id, references(:github_repos, on_delete: :delete_all) 8 | 9 | timestamps() 10 | end 11 | 12 | create unique_index(:project_github_repos, [:project_id, :github_repo_id]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170706132431_add_github_app_installation_sender_github_id.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubAppInstallationSenderGithubId do 2 | @moduledoc """ 3 | Migration to add a `sender_github_id` field to a `GithubAppInstallation`. 4 | This is required when creating an unmatced installation, to associate it with 5 | a github user at a later time. 6 | """ 7 | use Ecto.Migration 8 | 9 | def change do 10 | alter table(:github_app_installations) do 11 | add :sender_github_id, :integer 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170707213648_create_organization_github_app_installation.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateOrganizationGithubAppInstallation do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:organization_github_app_installations) do 6 | add :organization_id, references(:organizations, on_delete: :nothing) 7 | add :github_app_installation_id, references(:github_app_installations, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | create index(:organization_github_app_installations, [:organization_id]) 12 | create index(:organization_github_app_installations, [:github_app_installation_id]) 13 | 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170711122252_add_github_app_installation_origin_field.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubAppInstallationOriginField do 2 | @moduledoc """ 3 | Adds an origin field to the GithubAppInstallation 4 | 5 | The value of this field can be "codecorps" or "github" 6 | """ 7 | use Ecto.Migration 8 | 9 | def change do 10 | alter table(:github_app_installations) do 11 | add :origin, :string, null: false, default: "codecorps" 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170717092127_allow_user_email_username_null.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AllowUserEmailUsernameNull do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:users) do 6 | modify :email, :string, null: true 7 | modify :username, :string, null: true 8 | end 9 | end 10 | 11 | def down do 12 | alter table(:users) do 13 | modify :email, :string, null: false 14 | modify :username, :string, null: false 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170725060612_add_github_account_fields_to_github_app_installation.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubAccountFieldsToGithubAppInstallation do 2 | @moduledoc """ 3 | These fields are used to hold account information, to be displayed in the 4 | client UI. 5 | """ 6 | use Ecto.Migration 7 | 8 | def change do 9 | alter table(:github_app_installations) do 10 | add :github_account_avatar_url, :string 11 | add :github_account_id, :integer 12 | add :github_account_login, :string 13 | add :github_account_type, :string 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170727052644_create_organization_invite.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateOrganizationInvite do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:organization_invites) do 6 | add :code, :string, null: false 7 | add :email, :string, null: false 8 | add :title, :string, null: false 9 | add :fulfilled, :boolean, default: false, null: false 10 | 11 | timestamps() 12 | end 13 | create index(:organization_invites, [:code], unique: true) 14 | create index(:organization_invites, [:email]) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170731130121_remove_task_state.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveTaskState do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:tasks) do 6 | remove :state 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:tasks) do 12 | add :state, :string 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170814131722_link_task_to_github.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.LinkTaskToGithub do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:tasks) do 6 | add :github_repo_id, references(:github_repos) 7 | end 8 | 9 | rename table(:tasks), :github_id, to: :github_issue_number 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170913114958_remove_github_event_source_field.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveGithubEventSourceField do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:github_events) do 6 | remove :source 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:github_events) do 12 | add :source, :string 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170921014405_loosen_markdown_restrictions.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.LoosenMarkdownRestrictions do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:comments) do 6 | modify :markdown, :text, null: true 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:comments) do 12 | modify :markdown, :text, null: false 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170925214512_add_type_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddTypeToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :type, :string, default: "user" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170925230419_add_payload_to_git_hub_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddPayloadToGitHubEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_events) do 6 | add :payload, :map 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170926134646_add_failure_reason_to_github_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddFailureReasonToGithubEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_events) do 6 | add :failure_reason, :string 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20170927100300_add_closed_at_to_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddClosedAtToTasks do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:tasks) do 6 | add :closed_at, :utc_datetime 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /priv/repo/migrations/20170928234412_add_created_at_and_modified_at_to_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCreatedAtAndModifiedAtToTasks do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:tasks) do 6 | add :created_at, :utc_datetime 7 | add :modified_at, :utc_datetime 8 | add :created_from, :string, default: "code_corps" 9 | add :modified_from, :string, default: "code_corps" 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171003134956_add_unique_constraint_to_users_github_id.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUniqueConstraintToUsersGithubId do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create index(:users, [:github_id], unique: true) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171003225853_add_created_at_and_modified_at_to_comments.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddCreatedAtAndModifiedAtToComments do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:comments) do 6 | add :created_at, :utc_datetime 7 | add :modified_at, :utc_datetime 8 | add :created_from, :string, default: "code_corps" 9 | add :modified_from, :string, default: "code_corps" 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171006063358_add_archived_to_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddArchivedToTasks do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:tasks) do 6 | add :archived, :boolean, null: false, default: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171006161407_change_organization_invite_title_to_organization_name.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeOrganizationInviteTitleToOrganizationName do 2 | use Ecto.Migration 3 | 4 | def change do 5 | rename table(:organization_invites), :title, to: :organization_name 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171012215106_create_github_issues.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateGithubIssues do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_issues) do 6 | add :body, :text 7 | add :closed_at, :utc_datetime 8 | add :comments_url, :text 9 | add :events_url, :text 10 | add :github_created_at, :utc_datetime 11 | add :github_id, :integer 12 | add :github_updated_at, :utc_datetime 13 | add :html_url, :text 14 | add :labels_url, :text 15 | add :locked, :boolean 16 | add :number, :integer 17 | add :state, :string 18 | add :title, :text 19 | add :url, :text 20 | 21 | timestamps() 22 | 23 | add :github_repo_id, references(:github_repos) 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171012221231_change_github_issue_relationships.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeGithubIssueRelationships do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:tasks) do 6 | remove :github_issue_number 7 | add :github_issue_id, references(:github_issues) 8 | end 9 | end 10 | 11 | def down do 12 | alter table(:tasks) do 13 | add :github_issue_number, :integer 14 | remove :github_issue_id 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171016125229_add_github_comment.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubComment do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_comments) do 6 | add :body, :text 7 | add :github_created_at, :utc_datetime 8 | add :github_id, :integer 9 | add :github_updated_at, :utc_datetime 10 | add :html_url, :text 11 | add :url, :text 12 | 13 | timestamps() 14 | 15 | add :github_issue_id, references(:github_issues) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171016125516_change_github_comment_relationships.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeGithubCommentRelationships do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:comments) do 6 | remove :github_id 7 | add :github_comment_id, references(:github_comments) 8 | end 9 | end 10 | 11 | def down do 12 | alter table(:comments) do 13 | add :github_id, :integer 14 | remove :github_comment_id 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddPullRequestsToTasks do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:tasks) do 6 | add :github_pull_request_id, references(:github_pull_requests) 7 | end 8 | end 9 | 10 | def down do 11 | alter table(:tasks) do 12 | remove :github_pull_request_id 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171017235433_add_missing_github_id_indexes.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddMissingGithubIdIndexes do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create index(:github_comments, [:github_id], unique: true) 6 | create index(:github_issues, [:github_id], unique: true) 7 | create index(:github_repos, [:github_id], unique: true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171019191035_change_github_issue_and_github_pull_request_relationships.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeGithubIssueAndGithubPullRequestRelationships do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:tasks) do 6 | remove :github_pull_request_id 7 | end 8 | 9 | alter table(:github_issues) do 10 | add :github_pull_request_id, references(:github_pull_requests) 11 | end 12 | end 13 | 14 | def down do 15 | alter table(:tasks) do 16 | add :github_pull_request_id, references(:github_pull_requests) 17 | end 18 | 19 | alter table(:github_issues) do 20 | remove :github_pull_request_id 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171026010933_add_more_missing_indexes.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddMoreMissingIndexes do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create index(:tasks, [:archived]) 6 | create index(:tasks, [:status]) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171027061833_add_more_indexes_again.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddMoreIndexesAgain do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create index(:auth_token, [:value]) 6 | create index(:github_events, [:github_delivery_id], unique: true) 7 | create index(:github_events, [:status]) 8 | create index(:stripe_external_accounts, [:id_from_stripe], unique: true) 9 | create index(:stripe_file_upload, [:id_from_stripe], unique: true) 10 | create index(:tasks, [:number]) 11 | create index(:tasks, [:order]) 12 | create index(:task_lists, [:inbox]) 13 | create index(:task_lists, [:order]) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171030182857_add_sync_statuses_to_github_repo.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddSyncStatusesToGithubRepo do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_repos) do 6 | add :sync_state, :string, default: "unsynced" 7 | add :syncing_comments_count, :integer, default: 0 8 | add :syncing_issues_count, :integer, default: 0 9 | add :syncing_pull_requests_count, :integer, default: 0 10 | end 11 | 12 | create index(:github_repos, [:sync_state]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171031232023_add_sync_state_to_project_github_repos.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddSyncStateToProjectGithubRepos do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:project_github_repos) do 6 | add :sync_state, :string, default: "unsynced" 7 | end 8 | 9 | create index(:project_github_repos, [:sync_state]) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171101023309_add_github_repo_to_github_comments.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubRepoToGithubComments do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_comments) do 6 | add :github_repo_id, references(:github_repos, on_delete: :nothing) 7 | end 8 | 9 | create index(:github_comments, [:github_repo_id]) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171104013543_add_indexes_for_syncing.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddIndexesForSyncing do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create index(:tasks, [:github_issue_id, :project_id]) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171106103153_add_unique_constraints_to_specific_task_lists.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUniqueConstraintsToSpecificTaskLists do 2 | @moduledoc false 3 | 4 | use Ecto.Migration 5 | 6 | def change do 7 | # There is already a "task_lists_project_id_index", so we name explicitly 8 | 9 | create unique_index( 10 | "task_lists", [:project_id], 11 | where: "done = true", name: "task_lists_project_id_done_index") 12 | 13 | create unique_index( 14 | "task_lists", [:project_id], 15 | where: "pull_requests = true", name: "task_lists_project_id_pull_requests_index") 16 | 17 | create unique_index( 18 | "task_lists", [:project_id], 19 | where: "inbox = true", name: "task_lists_project_id_inbox_index") 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171106200036_archive_outdated_tasks.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ArchiveOutdatedTasks do 2 | use Ecto.Migration 3 | 4 | import Ecto.Query 5 | 6 | alias CodeCorps.Repo 7 | 8 | def up do 9 | from( 10 | t in "tasks", 11 | where: t.status == "closed", 12 | where: date_add(t.modified_at, 30, "day") > ^Date.utc_today, 13 | update: [set: [archived: true, task_list_id: nil]] 14 | ) |> Repo.update_all([]) 15 | end 16 | 17 | def down do 18 | # no-op 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171109231538_add_github_id_was.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddGithubIdWas do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:users) do 6 | add :github_id_was, :integer 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171110001134_add_index_for_github_id_was_to_users.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddIndexForGithubIdWasToUsers do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create unique_index(:users, [:github_id_was]) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171114010851_migrate_unsupported_github_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.MigrateUnsupportedGithubEvents do 2 | use Ecto.Migration 3 | 4 | import Ecto.Query 5 | 6 | alias CodeCorps.Repo 7 | 8 | def up do 9 | from( 10 | ge in "github_events", 11 | where: [failure_reason: "not_fully_implemented"], 12 | or_where: [failure_reason: "not_yet_implemented"], 13 | or_where: [failure_reason: "unexpected_action"], 14 | update: [set: [failure_reason: nil, status: "unsupported"]] 15 | ) |> Repo.update_all([]) 16 | end 17 | 18 | def down do 19 | # no-op 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171114033357_add_unique_constraint_for_project_github_repo_and_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddUniqueConstraintForProjectGithubRepoAndProject do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop_if_exists index(:project_github_repos, [:project_id, :github_repo_id], unique: true) 6 | drop_if_exists index(:project_github_repos, [:github_repo_id], unique: true) 7 | create unique_index(:project_github_repos, [:github_repo_id]) 8 | end 9 | 10 | def down do 11 | drop_if_exists index(:project_github_repos, [:github_repo_id], unique: true) 12 | create unique_index(:project_github_repos, [:project_id, :github_repo_id]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171114225214_add_project_id_to_github_repo.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddProjectIdToGithubRepo do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_repos) do 6 | add :project_id, references(:projects, on_delete: :nothing) 7 | end 8 | 9 | create index(:github_repos, [:project_id]) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171114232534_remove_project_github_repos.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveProjectGithubRepos do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop table(:project_github_repos) 6 | end 7 | 8 | def down do 9 | create table(:project_github_repos) do 10 | add :project_id, references(:projects, on_delete: :delete_all) 11 | add :github_repo_id, references(:github_repos, on_delete: :delete_all) 12 | add :sync_state, :string, default: "unsynced" 13 | 14 | timestamps() 15 | end 16 | 17 | create unique_index(:project_github_repos, [:project_id, :github_repo_id]) 18 | create index(:project_github_repos, [:sync_state]) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171115201624_drop_github_repos_project_id_unique_index_if_exists.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.DropGithubReposProjectIdUniqueIndexIfExists do 2 | use Ecto.Migration 3 | 4 | def up do 5 | drop_if_exists index(:github_repos, [:project_id], unique: true) 6 | create_if_not_exists index(:github_repos, [:project_id]) 7 | end 8 | 9 | def down do 10 | # no-op 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171115225358_add_serialized_error_to_github_events.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddSerializedErrorToGithubEvents do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:github_events) do 6 | add :data, :text 7 | add :error, :text 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171119004204_create_github_issue_assignees.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateGithubIssueAssignees do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:github_issue_assignees) do 6 | add :github_issue_id, references(:github_issues, on_delete: :nothing) 7 | add :github_user_id, references(:github_users, on_delete: :nothing) 8 | 9 | timestamps() 10 | end 11 | 12 | create index(:github_issue_assignees, [:github_issue_id]) 13 | create index(:github_issue_assignees, [:github_user_id]) 14 | create unique_index(:github_issue_assignees, [:github_issue_id, :github_user_id]) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171121075226_migrate_stripe_connect_accounts.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.MigrateStripeConnectAccounts do 2 | use Ecto.Migration 3 | 4 | def change do 5 | rename table(:stripe_connect_accounts), :transfers_enabled, to: :payouts_enabled 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171123065902_remove_github_repo_and_owner_from_project.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.RemoveGithubRepoAndOwnerFromProject do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | remove(:github_repo) 7 | remove(:github_owner) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171127215847_change_organization_invite_fulfillment.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.ChangeOrganizationInviteFulfillment do 2 | use Ecto.Migration 3 | 4 | def up do 5 | alter table(:organization_invites) do 6 | add :organization_id, references(:organizations, on_delete: :nothing) 7 | remove :fulfilled 8 | end 9 | 10 | create index(:organization_invites, [:organization_id], unique: true) 11 | end 12 | 13 | def down do 14 | drop_if_exists index(:organization_invites, [:organization_id], unique: true) 15 | 16 | alter table(:organization_invites) do 17 | remove :organization_id 18 | add :fulfilled, :boolean, default: false 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171201073818_add_approval_requested_to_projects.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddApprovalRequestedToProjects do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:projects) do 6 | add :approval_requested, :boolean, default: false 7 | end 8 | create index(:projects, [:approval_requested]) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171205161052_create_messages.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.CreateMessages do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:messages) do 6 | add :body, :text 7 | add :initiated_by, :string 8 | add :subject, :text 9 | add :author_id, references(:users, on_delete: :nothing) 10 | add :project_id, references(:projects, on_delete: :nothing) 11 | 12 | timestamps() 13 | end 14 | 15 | create index(:messages, [:author_id]) 16 | create index(:messages, [:initiated_by]) 17 | create index(:messages, [:project_id]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /priv/repo/migrations/20171220154922_add_part_type_to_conversation.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.AddPartTypeToConversation do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table(:conversation_parts) do 6 | add :part_type, :string, default: "comment" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20180113002017_normalize_organization_user_type.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Repo.Migrations.NormalizeOrganizationUserType do 2 | use Ecto.Migration 3 | 4 | def up do 5 | execute( 6 | """ 7 | UPDATE github_users 8 | SET type = 'organization' 9 | WHERE type = 'Organization' 10 | """ 11 | ) 12 | end 13 | 14 | def down do 15 | execute( 16 | """ 17 | UPDATE github_users 18 | SET type = 'Organization' 19 | WHERE type = 'organization' 20 | """ 21 | ) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /priv/static/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/fixtures/github/app.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp 3 | wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5 4 | 1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh 5 | 3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2 6 | pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX 7 | GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il 8 | AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF 9 | L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k 10 | X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl 11 | U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ 12 | 37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0= 13 | -----END RSA PRIVATE KEY----- 14 | -------------------------------------------------------------------------------- /test/fixtures/github/endpoints/forbidden.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Maximum number of login attempts exceeded. Please try again later.", 3 | "documentation_url": "https://developer.github.com/v3" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/github/endpoints/installations_access_tokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "v1.1f699f1069f60xxx", 3 | "expires_at": "2016-07-11T22:14:10Z" 4 | } 5 | -------------------------------------------------------------------------------- /test/lib/code_corps/accounts/users_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Accounts.UsersTest do 2 | @moduledoc false 3 | 4 | use CodeCorps.DbAccessCase 5 | import CodeCorps.TestHelpers, only: [assert_ids_from_query: 2] 6 | 7 | alias CodeCorps.{Accounts, User} 8 | 9 | describe "project_filter/2" do 10 | test "filters users by project filter" do 11 | user_1 = insert(:user) 12 | user_2 = insert(:user) 13 | 14 | project = insert(:project) 15 | 16 | insert(:project_user, user: user_1, project: project) 17 | insert(:project_user, user: user_2, project: project) 18 | insert(:project_user) 19 | 20 | result = 21 | User 22 | |> Accounts.Users.project_filter(%{"project_id" => project.id}) 23 | |> Repo.all() 24 | 25 | assert_ids_from_query(result, [user_1.id, user_2.id]) 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /test/lib/code_corps/adapter/map_transformer_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Adapter.MapTransformerTest do 2 | use ExUnit.Case 3 | 4 | alias CodeCorps.Adapter.MapTransformer 5 | 6 | @mapping [{:id, ["id"]}, {:user_id, ["user", "id"]}] 7 | 8 | describe "transform/2" do 9 | test "transforms map correctly" do 10 | map = %{"id" => 1, "user" => %{"id" => 1}} 11 | 12 | assert MapTransformer.transform(map, @mapping) == %{id: 1, user_id: 1} 13 | end 14 | end 15 | 16 | describe "transform_inverse/2" do 17 | test "inverse transforms map correctly" do 18 | map = %{id: 1, user_id: 1} 19 | 20 | assert MapTransformer.transform_inverse(map, @mapping) == %{"id" => 1, "user" => %{"id" => 1}} 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/lib/code_corps/analytics/segment_data_extractor_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Analytics.SegmentDataExtractorTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.Factories 7 | 8 | alias CodeCorps.Analytics.SegmentDataExtractor 9 | 10 | describe "get_project_id/1" do 11 | test "should return correct id for project user" do 12 | project_user = build(:project_user) 13 | project_id = "project_#{project_user.project_id}" 14 | 15 | assert SegmentDataExtractor.get_project_id(project_user) == project_id 16 | end 17 | 18 | test "should return nil for unknown resource" do 19 | assert SegmentDataExtractor.get_project_id(%{}) == nil 20 | end 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /test/lib/code_corps/cloudex/cloudinary_url_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Cloudex.CloudinaryUrlTest do 2 | alias CodeCorps.Cloudex.CloudinaryUrl 3 | use ExUnit.Case, async: true 4 | 5 | test "calls Cloudex.Url.for with correct arguments" do 6 | expected_url = "https://placehold.it/100x100" 7 | url = CloudinaryUrl.for(:test_public_id, %{height: 100, width: 100}, nil, nil, nil) 8 | assert expected_url == url 9 | end 10 | 11 | test "call Cloudex.Url.for insert https://" do 12 | expected_url = "https://placehold.it/100x100" 13 | url = CloudinaryUrl.for("//placehold.it/100x100", %{height: 100, width: 100}, nil, nil, nil) 14 | assert expected_url == url 15 | end 16 | 17 | test "returns correct url if called without public_id" do 18 | expected_url = "#{Application.get_env(:code_corps, :asset_host)}/icons/type1_default_version1_color1.png" 19 | url = CloudinaryUrl.for(nil, %{}, "version1", "color1", "type1") 20 | assert expected_url == url 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/lib/code_corps/cloudex/uploader_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Cloudex.UploaderTest do 2 | alias CodeCorps.Cloudex.Uploader 3 | use ExUnit.Case, async: true 4 | 5 | test "returns the public_id" do 6 | {:ok, %Cloudex.UploadedImage{public_id: public_id}} = 7 | "https://placehold.it/500x500" 8 | |> Uploader.upload 9 | 10 | assert public_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/lib/code_corps/conn_utils_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.ConnUtilsTest do 2 | use CodeCorpsWeb.ConnCase 3 | 4 | alias CodeCorps.ConnUtils 5 | 6 | defp conn_with_ip(ip) do 7 | %Plug.Conn{remote_ip: ip} 8 | end 9 | 10 | describe "extract_ip/1" do 11 | test "extracts IP address from the request properly" do 12 | assert conn_with_ip({151, 236, 219, 228}) |> ConnUtils.extract_ip == "151.236.219.228" 13 | end 14 | end 15 | 16 | describe "extract_user_agent/1" do 17 | test "extracts User Agent from the request properly", %{conn: conn} do 18 | assert conn |> put_req_header("user-agent", "Some agent") |> ConnUtils.extract_user_agent == "Some agent" 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/lib/code_corps/emails/base_email_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.BaseEmailTest do 2 | use CodeCorps.ModelCase 3 | use Bamboo.Test 4 | alias CodeCorps.Emails.BaseEmail 5 | 6 | describe "get_name/1" do 7 | test "get_name returns there on nil name" do 8 | user = %CodeCorps.User{} 9 | assert BaseEmail.get_name(user) == "there" 10 | end 11 | 12 | test "get_name returns first_name of user" do 13 | user = %CodeCorps.User{first_name: "Zacck"} 14 | assert BaseEmail.get_name(user) == "Zacck" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/emails/forgot_password_email_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.ForgotPasswordEmailTest do 2 | use CodeCorps.ModelCase 3 | use Bamboo.Test 4 | 5 | alias CodeCorps.{AuthToken, Emails.ForgotPasswordEmail, WebClient} 6 | 7 | test "forgot password email works" do 8 | user = insert(:user) 9 | { :ok, %AuthToken{ value: token } } = AuthToken.changeset(%AuthToken{}, user) |> Repo.insert 10 | 11 | email = ForgotPasswordEmail.create(user, token) 12 | assert email.from == "Code Corps" 13 | assert email.to == user.email 14 | { :link, encoded_link } = email.private.template_model |> Enum.at(0) 15 | assert "#{WebClient.url()}/password/reset?token=#{token}" == encoded_link 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/emails/message_initiated_by_project_email_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.MessageInitiatedByProjectEmailTest do 2 | use CodeCorps.DbAccessCase 3 | use Bamboo.Test 4 | 5 | alias CodeCorps.Emails.MessageInitiatedByProjectEmail 6 | 7 | test "email works" do 8 | %{message: message} = conversation = 9 | insert(:conversation) 10 | |> Repo.preload([[message: :project], :user]) 11 | 12 | email = MessageInitiatedByProjectEmail.create(message, conversation) 13 | assert email.from == "Code Corps" 14 | assert email.to == conversation.user.email 15 | 16 | template_model = email.private.template_model 17 | 18 | assert template_model == %{ 19 | conversation_url: "http://localhost:4200/conversations/#{conversation.id}", 20 | name: conversation.user.first_name, 21 | project_title: message.project.title, 22 | subject: "You have a new message from #{message.project.title}" 23 | } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/lib/code_corps/emails/organization_invite_email_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Emails.OrganizationInviteEmailTest do 2 | use CodeCorps.ModelCase 3 | use Bamboo.Test 4 | 5 | alias CodeCorps.{Emails.OrganizationInviteEmail, WebClient} 6 | 7 | test "organization email invite works" do 8 | invite = insert(:organization_invite) 9 | email = OrganizationInviteEmail.create(invite) 10 | 11 | assert email.from == "Code Corps" 12 | assert email.to == invite.email 13 | 14 | template_model = email.private.template_model 15 | params = 16 | %{code: invite.code, organization_name: invite.organization_name} 17 | |> URI.encode_query 18 | invite_url = "#{WebClient.url()}/organizations/new?#{params}" 19 | 20 | assert template_model == %{ 21 | invite_url: invite_url, 22 | organization_name: invite.organization_name, 23 | subject: "Create your first project on Code Corps" 24 | } 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/adapters/app_installation_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Adapters.AppInstallationTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Adapters.AppInstallation 9 | 10 | describe "from_installation_event/1" do 11 | test "maps api payload correctly" do 12 | payload = load_event_fixture("installation_created") 13 | 14 | assert AppInstallation.from_installation_event(payload) == %{ 15 | github_id: payload["installation"]["id"], 16 | github_account_id: payload["installation"]["account"]["id"], 17 | github_account_login: payload["installation"]["account"]["login"], 18 | github_account_avatar_url: payload["installation"]["account"]["avatar_url"], 19 | github_account_type: payload["installation"]["account"]["type"], 20 | sender_github_id: payload["sender"]["id"], 21 | } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/adapters/repo_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Adapters.RepoTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Adapters.Repo 9 | 10 | describe "from_api/1" do 11 | test "maps api payload correctly" do 12 | %{"repositories" => [repo]} = load_event_fixture("user_repositories") 13 | 14 | assert Repo.from_api(repo) == %{ 15 | github_id: repo |> get_in(["id"]), 16 | name: repo |> get_in(["name"]), 17 | github_account_id: repo |> get_in(["owner", "id"]), 18 | github_account_login: repo |> get_in(["owner", "login"]), 19 | github_account_avatar_url: repo |> get_in(["owner", "avatar_url"]), 20 | github_account_type: repo |> get_in(["owner", "type"]), 21 | } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/event/installation/validator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Installation.ValidatorTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Event.Installation.Validator 9 | 10 | describe "valid?/1" do 11 | test "returns true for any Installation event fixture" do 12 | assert "installation_created" |> load_event_fixture() |> Validator.valid? 13 | end 14 | 15 | test "returns false for an unsupported structure" do 16 | refute Validator.valid?("foo") 17 | refute Validator.valid?(%{"foo" => "bar"}) 18 | refute Validator.valid?(%{"installation" => %{"bar" => "baz"}}) 19 | refute Validator.valid?(%{"sender" => %{"bar" => "baz"}}) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/event/issue_comment/validator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.IssueComment.ValidatorTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Event.IssueComment.Validator 9 | 10 | describe "valid?/1" do 11 | test "returns true for any Issues event fixture" do 12 | assert "issue_comment_created" |> load_event_fixture() |> Validator.valid? 13 | assert "issue_comment_deleted" |> load_event_fixture() |> Validator.valid? 14 | assert "issue_comment_edited" |> load_event_fixture() |> Validator.valid? 15 | end 16 | 17 | test "returns false for an unsupported structure" do 18 | refute Validator.valid?("foo") 19 | refute Validator.valid?(%{"foo" => "bar"}) 20 | refute Validator.valid?(%{"issue" => %{"bar" => "baz"}}) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/event/issues/validator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.Issues.ValidatorTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Event.Issues.Validator 9 | 10 | describe "valid?/1" do 11 | test "returns true for any Issues event fixture" do 12 | assert "issues_opened" |> load_event_fixture() |> Validator.valid? 13 | assert "issues_closed" |> load_event_fixture() |> Validator.valid? 14 | assert "issues_edited" |> load_event_fixture() |> Validator.valid? 15 | assert "issues_reopened" |> load_event_fixture() |> Validator.valid? 16 | end 17 | 18 | test "returns false for an unsupported structure" do 19 | refute Validator.valid?("foo") 20 | refute Validator.valid?(%{"foo" => "bar"}) 21 | refute Validator.valid?(%{"issue" => %{"bar" => "baz"}}) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/lib/code_corps/github/event/pull_request/validator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GitHub.Event.PullRequest.ValidatorTest do 2 | @moduledoc false 3 | 4 | use ExUnit.Case, async: true 5 | 6 | import CodeCorps.GitHub.TestHelpers 7 | 8 | alias CodeCorps.GitHub.Event.PullRequest.Validator 9 | 10 | describe "valid?/1" do 11 | test "returns true for any PullRequest event fixture" do 12 | assert "pull_request_opened" |> load_event_fixture() |> Validator.valid? 13 | assert "pull_request_closed" |> load_event_fixture() |> Validator.valid? 14 | assert "pull_request_edited" |> load_event_fixture() |> Validator.valid? 15 | assert "pull_request_reopened" |> load_event_fixture() |> Validator.valid? 16 | end 17 | 18 | test "returns false for an unsupported structure" do 19 | refute Validator.valid?("foo") 20 | refute Validator.valid?(%{"foo" => "bar"}) 21 | refute Validator.valid?(%{"issue" => %{"bar" => "baz"}}) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/lib/code_corps/helpers/random_icon_color_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RandomIconColor.RandomIconColorTest do 2 | use ExUnit.Case, async: true 3 | import CodeCorps.Helpers.RandomIconColor 4 | import Ecto.Changeset 5 | 6 | test "inserts color into changeset" do 7 | changeset = generate_icon_color(cast({%{}, %{}}, %{}, []), :color_key) 8 | assert get_field(changeset, :color_key) == "blue" 9 | end 10 | 11 | test "ignores invalid changeset" do 12 | changeset = {%{}, %{color_key: :required}} 13 | |> cast(%{}, []) 14 | |> validate_required(:color_key) 15 | assert generate_icon_color(changeset, :color_key) == changeset 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/map_utils_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.MapUtilsTest do 2 | use ExUnit.Case, async: true 3 | 4 | import CodeCorps.MapUtils, only: [keys_to_string: 1, rename: 3] 5 | 6 | test "&rename/3 renames old key in map to new key" do 7 | assert %{"foo" => 2} |> rename("foo", "bar") == %{"bar" => 2} 8 | end 9 | 10 | test "&keys_to_string/1 stringifies any keys in map" do 11 | assert %{:a => "one", :b => "two"} |> keys_to_string == %{"a" => "one", "b" => "two"} 12 | assert %{} |> keys_to_string == %{} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/lib/code_corps/messages/conversation_query_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Messages.ConversationQueryTest do 2 | use CodeCorps.DbAccessCase 3 | 4 | alias CodeCorps.{ 5 | Conversation, 6 | Messages.ConversationQuery, 7 | Repo 8 | } 9 | 10 | describe "status_filter/2" do 11 | test "filters by status" do 12 | open_conversation = insert(:conversation, status: "open") 13 | _closed_conversation = insert(:conversation, status: "closed") 14 | 15 | [result] = 16 | Conversation 17 | |> ConversationQuery.status_filter(%{"status" => "open"}) 18 | |> Repo.all() 19 | 20 | assert result.id == open_conversation.id 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/lib/code_corps/messages/conversations_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Messages.ConversationsTest do 2 | @moduledoc false 3 | 4 | import DateTime, only: [compare: 2] 5 | 6 | use CodeCorps.DbAccessCase 7 | 8 | alias CodeCorps.{ 9 | Conversation, Messages 10 | } 11 | 12 | describe "part_added_changeset/1" do 13 | test "sets the updated_at to the current time" do 14 | old_updated_at = DateTime.utc_now() |> Timex.shift(days: -5) 15 | conversation = %Conversation{updated_at: old_updated_at} 16 | changeset = conversation |> Messages.Conversations.part_added_changeset() 17 | assert compare(old_updated_at, changeset.changes[:updated_at]) == :lt 18 | end 19 | 20 | test "sets status to open" do 21 | conversation = %Conversation{status: "closed"} 22 | changeset = conversation |> Messages.Conversations.part_added_changeset() 23 | assert changeset.changes[:status] == "open" 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/auth_token_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.AuthTokenTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.AuthToken 5 | 6 | test "changeset with valid attributes" do 7 | user = insert(:user) 8 | changeset = AuthToken.changeset(%AuthToken{}, user) 9 | assert changeset.valid? 10 | assert changeset.changes.value 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/github_comment_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.GithubCommentTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.GithubComment 5 | 6 | @valid_attrs %{ 7 | body: "I'm having a problem with this.", 8 | github_created_at: "2011-04-22T13:33:48Z", 9 | github_id: 1, 10 | github_updated_at: "2014-03-03T18:58:10Z", 11 | html_url: "https://github.com/octocat/Hello-World/issues/1347", 12 | url: "https://api.github.com/repos/octocat/Hello-World/issues/1347", 13 | } 14 | @invalid_attrs %{} 15 | 16 | test "create_changeset/2 with valid attributes" do 17 | changeset = GithubComment.create_changeset(%GithubComment{}, @valid_attrs) 18 | assert changeset.valid? 19 | end 20 | 21 | test "create_changeset/2 with invalid attributes" do 22 | changeset = GithubComment.create_changeset(%GithubComment{}, @invalid_attrs) 23 | refute changeset.valid? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/preview_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.PreviewTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.Preview 5 | 6 | describe "create_changeset/2" do 7 | test "renders body html from markdown" do 8 | user = insert(:user) 9 | changeset = Preview.create_changeset(%Preview{}, %{ 10 | markdown: "A **strong** element", 11 | user_id: user.id 12 | }) 13 | assert changeset.valid? 14 | assert changeset |> get_change(:body) == "

A strong element

\n" 15 | end 16 | 17 | test "requires markdown change" do 18 | changeset = Preview.create_changeset(%Preview{}, %{}) 19 | refute changeset.valid? 20 | changeset |> assert_validation_triggered(:markdown, :required) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/role_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RoleTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.Role 5 | 6 | @valid_attrs %{ability: "Backend Development", kind: "technology", name: "Backend Developer"} 7 | @invalid_attrs %{ability: "Juggling", kind: "circus", name: "Juggler"} 8 | @empty_attrs %{} 9 | 10 | test "changeset with valid attributes" do 11 | changeset = Role.changeset(%Role{}, @valid_attrs) 12 | assert changeset.valid? 13 | end 14 | 15 | test "changeset with invalid attributes" do 16 | changeset = Role.changeset(%Role{}, @invalid_attrs) 17 | assert_error_message(changeset, :kind, "is invalid") 18 | refute changeset.valid? 19 | end 20 | 21 | test "changeset with empty attributes" do 22 | changeset = Role.changeset(%Role{}, @empty_attrs) 23 | refute changeset.valid? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/skill_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.SkillTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.Skill 5 | 6 | @valid_attrs %{ 7 | description: "Elixir is a functional, concurrent, general-purpose programming language that runs on the Erlang virtual machine (BEAM).", 8 | original_row: 1, 9 | title: "Elixir" 10 | } 11 | @invalid_attrs %{description: "test", original_row: 1} 12 | 13 | test "changeset with valid attributes" do 14 | changeset = Skill.changeset(%Skill{}, @valid_attrs) 15 | assert changeset.valid? 16 | end 17 | 18 | test "changeset with invalid attributes" do 19 | changeset = Skill.changeset(%Skill{}, @invalid_attrs) 20 | refute changeset.valid? 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/lib/code_corps/model/stripe_external_account_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeExternalAccountTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.StripeExternalAccount 5 | 6 | @valid_attrs %{account_id_from_stripe: "some content", id_from_stripe: "some content"} 7 | @invalid_attrs %{} 8 | 9 | test "changeset with valid attributes" do 10 | changeset = StripeExternalAccount.changeset(%StripeExternalAccount{}, @valid_attrs) 11 | assert changeset.valid? 12 | end 13 | 14 | test "changeset with invalid attributes" do 15 | changeset = StripeExternalAccount.changeset(%StripeExternalAccount{}, @invalid_attrs) 16 | refute changeset.valid? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/category_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.CategoryTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.Category, only: [create?: 1, update?: 1] 5 | 6 | describe "create?" do 7 | test "returns true when user is an admin" do 8 | user = build(:user, admin: true) 9 | assert create?(user) 10 | end 11 | 12 | test "returns false if user is not an admin" do 13 | user = build(:user, admin: false) 14 | refute create?(user) 15 | end 16 | end 17 | 18 | describe "update?" do 19 | test "returns true when user is an admin" do 20 | user = build(:user, admin: true) 21 | assert update?(user) 22 | end 23 | 24 | test "returns false if user is not an admin" do 25 | user = build(:user, admin: false) 26 | refute update?(user) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/organization_invite_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.OrganizationInvitePolicyTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.OrganizationInvite, only: [create?: 1, update?: 1] 5 | 6 | describe "create" do 7 | test "returns true when user is an admin" do 8 | user = build(:user, admin: true) 9 | assert create?(user) 10 | end 11 | 12 | test "returns false when user is not an admin" do 13 | user = build(:user, admin: false) 14 | refute create?(user) 15 | end 16 | end 17 | 18 | describe "update" do 19 | test "returns true when user is an admin" do 20 | user = insert(:user, admin: true) 21 | assert update?(user) 22 | end 23 | 24 | test "returns false when user is not an admin" do 25 | user = insert(:user, admin: false) 26 | refute update?(user) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/preview_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.PreviewTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.Preview, only: [create?: 2] 5 | 6 | describe "create?" do 7 | test "returns true if user is creating their own record" do 8 | user = insert(:user) 9 | 10 | params = %{"markdown" => "markdown", "user_id" => user.id} 11 | assert create?(user, params) 12 | end 13 | 14 | test "returns false if user is creating someone else's record" do 15 | [user, another_user] = insert_pair(:user) 16 | params = %{"markdown" => "markdown", "user_id" => another_user.id} 17 | refute create?(user, params) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/role_skill_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.RoleSkillTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.RoleSkill, only: [create?: 1, delete?: 1] 5 | 6 | describe "create?" do 7 | test "returns true when user is an admin" do 8 | user = build(:user, admin: true) 9 | assert create?(user) 10 | end 11 | 12 | test "returns false if user is not an admin" do 13 | user = build(:user, admin: false) 14 | refute create?(user) 15 | end 16 | end 17 | 18 | describe "delete?" do 19 | test "returns true when user is an admin" do 20 | user = build(:user, admin: true) 21 | assert delete?(user) 22 | end 23 | 24 | test "returns false if user is not an admin" do 25 | user = build(:user, admin: false) 26 | refute delete?(user) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/role_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.RoleTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.Role, only: [create?: 1] 5 | 6 | describe "create?" do 7 | test "returns true when user is an admin" do 8 | user = build(:user, admin: true) 9 | assert create?(user) 10 | end 11 | 12 | test "returns false if user is not an admin" do 13 | user = build(:user, admin: false) 14 | refute create?(user) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/skill_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.SkillTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.Skill, only: [create?: 1] 5 | 6 | describe "create?" do 7 | test "returns true when user is an admin" do 8 | user = build(:user, admin: true) 9 | assert create?(user) 10 | end 11 | 12 | test "returns false if user is not an admin" do 13 | user = build(:user, admin: false) 14 | refute create?(user) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/policy/user_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.Policy.UserTest do 2 | use CodeCorps.PolicyCase 3 | 4 | import CodeCorps.Policy.User, only: [update?: 2] 5 | 6 | describe "update?" do 7 | test "returns true if user is updating their own record" do 8 | user = insert(:user) 9 | assert update?(user, user) 10 | end 11 | 12 | test "returns false if user is updating someone else's record" do 13 | [user, another_user] = insert_pair(:user) 14 | refute update?(user, another_user) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps/random_icon_color/generator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RandomIconColor.GeneratorTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias CodeCorps.RandomIconColor.Generator 5 | 6 | @colors ~w(blue green light_blue pink purple yellow) 7 | 8 | test "generates random icon color" do 9 | assert Enum.member?(@colors, Generator.generate) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/lib/code_corps/random_icon_color/test_generator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.RandomIconColor.TestGeneratorTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias CodeCorps.RandomIconColor.TestGenerator 5 | 6 | @colors ~w(blue green light_blue pink purple yellow) 7 | 8 | test "generates random icon color" do 9 | assert Enum.member?(@colors, TestGenerator.generate) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/lib/code_corps/stripe_service/stripe_connect_external_account_service_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeService.StripeConnectExternalAccountServiceTest do 2 | use CodeCorps.ModelCase 3 | 4 | alias CodeCorps.StripeService.StripeConnectExternalAccountService 5 | 6 | describe "create" do 7 | test "creates a StripeExternalAccount" do 8 | api_external_account = %Stripe.BankAccount{id: "bnk_123"} 9 | local_connect_account = insert(:stripe_connect_account) 10 | 11 | {:ok, %CodeCorps.StripeExternalAccount{} = external_account} = 12 | StripeConnectExternalAccountService.create(api_external_account, local_connect_account) 13 | 14 | assert external_account.id_from_stripe == "bnk_123" 15 | assert external_account.stripe_connect_account_id == local_connect_account.id 16 | assert external_account.account_id_from_stripe == local_connect_account.id_from_stripe 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/controllers/preview_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PreviewControllerTest do 2 | use CodeCorpsWeb.ApiCase, resource_name: :preview 3 | 4 | @valid_attrs %{markdown: "A **strong** element"} 5 | 6 | describe "create" do 7 | @tag :authenticated 8 | test "creates and renders resource when data is valid", %{conn: conn, current_user: current_user} do 9 | attrs = @valid_attrs |> Map.merge(%{user: current_user}) 10 | assert conn |> request_create(attrs) |> json_response(201) 11 | end 12 | 13 | test "does not create resource, and responds with 401 when unauthenticated", %{conn: conn} do 14 | assert conn |> request_create(@valid_attrs) |> json_response(401) 15 | end 16 | 17 | @tag :authenticated 18 | test "does not update resource and renders 403 when not authorized", %{conn: conn} do 19 | assert conn |> request_create(@valid_attrs) |> json_response(403) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/controllers/slugged_route_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.SluggedRouteControllerTest do 2 | use CodeCorpsWeb.ApiCase, resource_name: :slugged_route 3 | 4 | test "shows chosen resource", %{conn: conn} do 5 | slug = "test-slug" 6 | slugged_route = insert(:slugged_route, slug: slug) 7 | conn 8 | |> get("/#{slug}") 9 | |> json_response(200) 10 | |> assert_id_from_response(slugged_route.id) 11 | end 12 | 13 | test "is case insensitive", %{conn: conn} do 14 | slug = "test" 15 | insert(:slugged_route, slug: slug) 16 | 17 | assert conn |> get("/test") |> json_response(200) 18 | assert conn |> get("/tEst") |> json_response(200) 19 | end 20 | 21 | test "renders 404 when id is nonexistent", %{conn: conn} do 22 | assert conn |> request_show(:not_found) |> json_response(404) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/plugs/current_user_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.Plug.CurrentUserTest do 2 | use CodeCorpsWeb.ConnCase 3 | 4 | test "sets conn.assigns[:current_user] if user is authenticated" do 5 | user = build(:user, first_name: "John"); 6 | conn = CodeCorps.Guardian.Plug.put_current_resource(build_conn(), user) 7 | result_conn = CodeCorpsWeb.Plug.CurrentUser.call(conn, []) 8 | assert result_conn.assigns[:current_user] == user 9 | end 10 | 11 | test "simply returns conn if user is not authenticated" do 12 | conn = build_conn() 13 | result_conn = CodeCorpsWeb.Plug.CurrentUser.call(conn, []) 14 | assert result_conn == conn 15 | refute result_conn.assigns[:current_user] 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/views/layout_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.LayoutViewTest do 2 | use CodeCorpsWeb.ViewCase 3 | end 4 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/views/page_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PageViewTest do 2 | use CodeCorpsWeb.ViewCase 3 | end 4 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/views/password_reset_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PasswordResetViewTest do 2 | use CodeCorpsWeb.ViewCase 3 | 4 | test "renders show" do 5 | args = %{ 6 | email: "test@test.com", 7 | token: "abc123", 8 | user_id: 123 9 | } 10 | 11 | rendered_json = render(CodeCorpsWeb.PasswordResetView, "show.json", args) 12 | 13 | expected_json = %{ 14 | email: "test@test.com", 15 | token: "abc123", 16 | user_id: 123 17 | } 18 | 19 | assert expected_json == rendered_json 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/views/password_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.PasswordViewTest do 2 | use CodeCorpsWeb.ViewCase 3 | 4 | test "renders show" do 5 | email = "wat@codecorps.org" 6 | 7 | rendered_json = render(CodeCorpsWeb.PasswordView, "show.json", %{email: email}) 8 | 9 | expected_json = %{ 10 | email: email 11 | } 12 | 13 | assert expected_json == rendered_json 14 | refute Map.has_key?(expected_json, :token) 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /test/lib/code_corps_web/views/user_role_view_test.exs: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.UserRoleViewTest do 2 | use CodeCorpsWeb.ViewCase 3 | 4 | test "renders all attributes and relationships properly" do 5 | user_role = insert(:user_role) 6 | 7 | rendered_json = render(CodeCorpsWeb.UserRoleView, "show.json-api", data: user_role) 8 | 9 | expected_json = %{ 10 | "data" => %{ 11 | "id" => user_role.id |> Integer.to_string, 12 | "type" => "user-role", 13 | "attributes" => %{}, 14 | "relationships" => %{ 15 | "role" => %{ 16 | "data" => %{"id" => user_role.role_id |> Integer.to_string, "type" => "role"} 17 | }, 18 | "user" => %{ 19 | "data" => %{"id" => user_role.user_id |> Integer.to_string, "type" => "user"} 20 | } 21 | } 22 | }, 23 | "jsonapi" => %{ 24 | "version" => "1.0" 25 | } 26 | } 27 | 28 | assert rendered_json == expected_json 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/support/authentication_test_helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.AuthenticationTestHelpers do 2 | use Phoenix.ConnTest 3 | import CodeCorps.Factories 4 | 5 | def authenticate(conn) do 6 | user = insert(:user) 7 | 8 | conn 9 | |> authenticate(user) 10 | end 11 | 12 | def authenticate(conn, user) do 13 | {:ok, token, _} = user |> CodeCorps.Guardian.encode_and_sign() 14 | 15 | conn 16 | |> put_req_header("authorization", "Bearer #{token}") 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/support/db_access_case.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.DbAccessCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests that require working with the database. 5 | """ 6 | 7 | use ExUnit.CaseTemplate 8 | 9 | using do 10 | quote do 11 | alias CodeCorps.Repo 12 | 13 | import CodeCorps.Factories 14 | end 15 | end 16 | 17 | setup tags do 18 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(CodeCorps.Repo) 19 | 20 | unless tags[:async] do 21 | Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, {:shared, self()}) 22 | end 23 | 24 | :ok 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/support/policy_case.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.PolicyCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | policy tests. 5 | 6 | You may define functions here to be used as helpers in 7 | your model tests. See `errors_on/2`'s definition as reference. 8 | 9 | Finally, if the test case interacts with the database, 10 | it cannot be async. For this reason, every test runs 11 | inside a transaction which is reset at the beginning 12 | of the test unless the test case is marked as async. 13 | """ 14 | 15 | use ExUnit.CaseTemplate 16 | 17 | using do 18 | quote do 19 | import CodeCorps.Factories 20 | import CodeCorps.PolicyCase 21 | end 22 | end 23 | 24 | setup tags do 25 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(CodeCorps.Repo) 26 | 27 | unless tags[:async] do 28 | Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, {:shared, self()}) 29 | end 30 | 31 | :ok 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/support/stripe_case.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.StripeCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests involving the stripe service. 5 | 6 | Basically a stripped down `CodeCorps.ModelCase` 7 | """ 8 | 9 | use ExUnit.CaseTemplate 10 | 11 | using do 12 | quote do 13 | alias CodeCorps.Repo 14 | 15 | import Ecto 16 | import Ecto.Changeset 17 | import Ecto.Query 18 | import CodeCorps.Factories 19 | import CodeCorps.StripeCase 20 | end 21 | end 22 | 23 | setup tags do 24 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(CodeCorps.Repo) 25 | 26 | unless tags[:async] do 27 | Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, {:shared, self()}) 28 | end 29 | 30 | :ok 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/support/test_environment_helper.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorps.TestEnvironmentHelper do 2 | def modify_env(app, overrides) do 3 | original_env = Application.get_all_env(app) 4 | Enum.each(overrides, fn {key, value} -> Application.put_env(app, key, value) end) 5 | 6 | ExUnit.Callbacks.on_exit(fn -> 7 | Enum.each overrides, fn {key, _} -> 8 | if Keyword.has_key?(original_env, key) do 9 | Application.put_env(app, key, Keyword.fetch!(original_env, key)) 10 | else 11 | Application.delete_env(app, key) 12 | end 13 | end 14 | end) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/support/view_case.ex: -------------------------------------------------------------------------------- 1 | defmodule CodeCorpsWeb.ViewCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests for views defined in the application. 5 | """ 6 | 7 | use ExUnit.CaseTemplate 8 | 9 | using do 10 | quote do 11 | import CodeCorps.Factories 12 | import Phoenix.View, only: [render: 3] 13 | end 14 | end 15 | 16 | setup tags do 17 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(CodeCorps.Repo) 18 | 19 | unless tags[:async] do 20 | Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, {:shared, self()}) 21 | end 22 | 23 | :ok 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | # Make sure all required plugins start before tests start running 2 | # Needs to be called before ExUnit.start 3 | {:ok, _} = Application.ensure_all_started(:ex_machina) 4 | {:ok, _} = Application.ensure_all_started(:bypass) 5 | 6 | ExUnit.configure exclude: [acceptance: true] 7 | ExUnit.start 8 | 9 | Ecto.Adapters.SQL.Sandbox.mode(CodeCorps.Repo, :manual) 10 | --------------------------------------------------------------------------------