From fdf2a812bded21f16031c32a297b789c3bfcc11a Mon Sep 17 00:00:00 2001 From: Ameya Vaichalkar <57044378+ameyagv@users.noreply.github.com> Date: Sun, 13 Oct 2024 11:59:39 -0400 Subject: [PATCH] E2424 (#120) * bookmark scaffold files created * Create bookmarks_controller * crud ops * add score related functions for bookmark * Create bookmarks_controller_spec.rb * Adding setup.sh to automate the setup inside containers (#84) * Adding files to setup * Updating setup script with working seed file * Adding a setup script to try to automate setup * Adding setup.sh as entrypoint * add db creation initilization script --------- Co-authored-by: Muhammad Ali Qureshi Co-authored-by: ameyagv * deleted unnecessary files * modified sign_up_topic * removed gemfile.lock and schema.rb * added gemfile.lock and schema.rb * changes bookmarks controller * added bookmark rating functions * added bookmark controller tests * schema update * swagger config update * resovle sign_up_topic migration * update schema.rb --------- Co-authored-by: Akshat Nitin Savla Co-authored-by: Mitanshu Reshamwala <42906426+MitanshuShaBa@users.noreply.github.com> Co-authored-by: Mitanshu Reshamwala Co-authored-by: vyshnavi-adusumelli <35067896+vyshnavi-adusumelli@users.noreply.github.com> Co-authored-by: Ali Qureshi <61347679+qureshi-ali@users.noreply.github.com> Co-authored-by: Muhammad Ali Qureshi --- Gemfile.lock | 3 + .../api/v1/bookmarks_controller.rb | 92 ++ app/models/bookmark.rb | 8 + app/models/bookmark_rating.rb | 4 + app/models/sign_up_topic.rb | 4 +- config/routes.rb | 7 + .../20231129050431_create_sign_up_topics.rb | 10 +- db/migrate/20240318205124_create_bookmarks.rb | 13 + .../20240324000112_create_bookmark_ratings.rb | 13 + db/schema.rb | 107 +- spec/factories.rb | 10 +- .../api/v1/bookmarks_controller_spec.rb | 279 ++++ spec/swagger_helper.rb | 12 + swagger/v1/swagger.yaml | 1310 +++++++++++++++++ 14 files changed, 1861 insertions(+), 11 deletions(-) create mode 100644 app/controllers/api/v1/bookmarks_controller.rb create mode 100644 app/models/bookmark.rb create mode 100644 app/models/bookmark_rating.rb create mode 100644 db/migrate/20240318205124_create_bookmarks.rb create mode 100644 db/migrate/20240324000112_create_bookmark_ratings.rb create mode 100644 spec/requests/api/v1/bookmarks_controller_spec.rb create mode 100644 swagger/v1/swagger.yaml diff --git a/Gemfile.lock b/Gemfile.lock index f5506b4dc..807f4defa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -132,6 +132,8 @@ GEM racc (~> 1.4) nokogiri (1.15.2-x64-mingw-ucrt) racc (~> 1.4) + nokogiri (1.15.2-x86_64-linux) + racc (~> 1.4) parallel (1.23.0) parser (3.2.2.3) ast (~> 2.4.1) @@ -242,6 +244,7 @@ GEM PLATFORMS aarch64-linux x64-mingw-ucrt + x86_64-linux DEPENDENCIES bcrypt (~> 3.1.7) diff --git a/app/controllers/api/v1/bookmarks_controller.rb b/app/controllers/api/v1/bookmarks_controller.rb new file mode 100644 index 000000000..93964a254 --- /dev/null +++ b/app/controllers/api/v1/bookmarks_controller.rb @@ -0,0 +1,92 @@ +class Api::V1::BookmarksController < ApplicationController + + # Index method returns the list of JSON objects of the bookmark + # GET on /bookmarks + def index + @bookmarks = Bookmark.order(:id) + render json: @bookmarks, status: :ok and return + end + + # Show method returns the JSON object of bookmark with id = {:id} + # GET on /bookmarks/:id + def show + begin + @bookmark = Bookmark.find(params[:id]) + render json: @bookmark, status: :ok and return + rescue ActiveRecord::RecordNotFound + render json: $ERROR_INFO.to_s, status: :not_found and return + end + end + + # Create method creates a bookmark and returns the JSON object of the created bookmark + # POST on /bookmarks + def create + begin + # params[:user_id] = @current_user.id + @bookmark = Bookmark.new(bookmark_params) + @bookmark.user_id = @current_user.id + @bookmark.save! + render json: @bookmark, status: :created and return + rescue ActiveRecord::RecordInvalid + render json: $ERROR_INFO.to_s, status: :unprocessable_entity + end + end + + # Update method updates the bookmark object with id - {:id} and returns the updated bookmark JSON object + # PUT on /bookmarks/:id + def update + @bookmark = Bookmark.find(params[:id]) + if @bookmark.update(update_bookmark_params) + render json: @bookmark, status: :ok + else + render json: @bookmark.errors.full_messages, status: :unprocessable_entity + end + end + + # Destroy method deletes the bookmark object with id- {:id} + # DELETE on /bookmarks/:id + def destroy + begin + @bookmark = Bookmark.find(params[:id]) + @bookmark.delete + rescue ActiveRecord::RecordNotFound + render json: $ERROR_INFO.to_s, status: :not_found and return + end + end + + # get_bookmark_rating_score method gets the bookmark rating of the bookmark object with id- {:id} + # GET on /bookmarks/:id/bookmarkratings + def get_bookmark_rating_score + begin + @bookmark = Bookmark.find(params[:id]) + @bookmark_rating = BookmarkRating.where(bookmark_id: @bookmark.id, user_id: @current_user.id).first + render json: @bookmark_rating, status: :ok and return + rescue ActiveRecord::RecordNotFound + render json: $ERROR_INFO.to_s, status: :not_found and return + end + end + + # save_bookmark_rating_score method creates or updates the bookmark rating of the bookmark object with id- {:id} + # POST on /bookmarks/:id/bookmarkratings + def save_bookmark_rating_score + @bookmark = Bookmark.find(params[:id]) + @bookmark_rating = BookmarkRating.where(bookmark_id: @bookmark.id, user_id: @current_user.id).first + if @bookmark_rating.blank? + @bookmark_rating = BookmarkRating.create(bookmark_id: @bookmark.id, user_id: @current_user.id, rating: params[:rating]) + else + @bookmark_rating.update({'rating': params[:rating].to_i}) + end + render json: {"bookmark": @bookmark, "rating": @bookmark_rating}, status: :ok + end + + private + + def bookmark_params + params.require(:bookmark).permit(:url, :title, :description, :topic_id, :rating, :id) + end + + def update_bookmark_params + params.require(:bookmark).permit(:url, :title, :description) + end + +end diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb new file mode 100644 index 000000000..2f02eabb5 --- /dev/null +++ b/app/models/bookmark.rb @@ -0,0 +1,8 @@ +class Bookmark < ApplicationRecord + belongs_to :user + # belongs_to :topic, class_name: "SignUpTopic" + has_many :bookmark_ratings + validates :url, presence: true + validates :title, presence: true + validates :description, presence: true +end diff --git a/app/models/bookmark_rating.rb b/app/models/bookmark_rating.rb new file mode 100644 index 000000000..d8e003f3e --- /dev/null +++ b/app/models/bookmark_rating.rb @@ -0,0 +1,4 @@ +class BookmarkRating < ApplicationRecord + belongs_to :bookmark + belongs_to :user +end diff --git a/app/models/sign_up_topic.rb b/app/models/sign_up_topic.rb index 0c5969432..366dc9f97 100644 --- a/app/models/sign_up_topic.rb +++ b/app/models/sign_up_topic.rb @@ -1,4 +1,6 @@ class SignUpTopic < ApplicationRecord - has_many :signed_up_teams, foreign_key: 'sign_up_topic_id', dependent: :destroy + has_many :signed_up_teams, foreign_key: 'topic_id', dependent: :destroy + has_many :teams, through: :signed_up_teams # list all teams choose this topic, no matter in waitlist or not + has_many :assignment_questionnaires, class_name: 'AssignmentQuestionnaire', foreign_key: 'topic_id', dependent: :destroy belongs_to :assignment end diff --git a/config/routes.rb b/config/routes.rb index f9ee5c4e3..134c56878 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,6 +40,13 @@ end end + resources :bookmarks, except: [:new, :edit] do + member do + get 'bookmarkratings', to: 'bookmarks#get_bookmark_rating_score' + post 'bookmarkratings', to: 'bookmarks#save_bookmark_rating_score' + end + end + resources :courses do collection do get ':id/add_ta/:ta_id', action: :add_ta diff --git a/db/migrate/20231129050431_create_sign_up_topics.rb b/db/migrate/20231129050431_create_sign_up_topics.rb index 1fb629acf..69ca12122 100644 --- a/db/migrate/20231129050431_create_sign_up_topics.rb +++ b/db/migrate/20231129050431_create_sign_up_topics.rb @@ -1,16 +1,16 @@ class CreateSignUpTopics < ActiveRecord::Migration[7.0] def change create_table :sign_up_topics do |t| - t.text :topic_name + t.text :topic_name, null: false t.references :assignment, null: false, foreign_key: true - t.integer :max_choosers + t.integer :max_choosers, default: 0, null: false t.text :category - t.string :topic_identifier - t.integer :micropayment + t.string :topic_identifier, limit: 10 + t.integer :micropayment, default: 0 t.integer :private_to t.text :description t.string :link - + t.index ["assignment_id"], name: "fk_sign_up_categories_sign_up_topics" t.timestamps end end diff --git a/db/migrate/20240318205124_create_bookmarks.rb b/db/migrate/20240318205124_create_bookmarks.rb new file mode 100644 index 000000000..b66641483 --- /dev/null +++ b/db/migrate/20240318205124_create_bookmarks.rb @@ -0,0 +1,13 @@ +class CreateBookmarks < ActiveRecord::Migration[7.0] + def change + create_table :bookmarks do |t| + t.text :url + t.text :title + t.text :description + t.integer :user_id + t.integer :topic_id + + t.timestamps + end + end +end diff --git a/db/migrate/20240324000112_create_bookmark_ratings.rb b/db/migrate/20240324000112_create_bookmark_ratings.rb new file mode 100644 index 000000000..037375693 --- /dev/null +++ b/db/migrate/20240324000112_create_bookmark_ratings.rb @@ -0,0 +1,13 @@ +class CreateBookmarkRatings < ActiveRecord::Migration[7.0] + def up + create_table "bookmark_ratings" do |t| + t.integer "bookmark_id" + t.integer "user_id" + t.integer "rating" + t.timestamps + end + end + def down + drop_table "bookmark_ratings" + end +end diff --git a/db/schema.rb b/db/schema.rb index 0ef599c47..deda3ae23 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_04_27_171632) do +ActiveRecord::Schema[7.0].define(version: 2024_03_24_000112) do create_table "account_requests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "username" t.string "full_name" @@ -42,6 +42,7 @@ t.integer "notification_limit", default: 15, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "used_in_round" t.index ["assignment_id"], name: "fk_aq_assignments_id" t.index ["questionnaire_id"], name: "fk_aq_questionnaire_id" end @@ -50,8 +51,6 @@ t.string "name" t.string "directory_path" t.integer "submitter_count" - t.integer "course_id" - t.integer "instructor_id" t.boolean "private" t.integer "num_reviews" t.integer "num_review_of_reviews" @@ -98,6 +97,31 @@ t.integer "sample_assignment_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "instructor_id", null: false + t.bigint "course_id" + t.boolean "enable_pair_programming", default: false + t.boolean "has_teams", default: false + t.boolean "has_topics", default: false + t.index ["course_id"], name: "index_assignments_on_course_id" + t.index ["instructor_id"], name: "index_assignments_on_instructor_id" + end + + create_table "bookmark_ratings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.integer "bookmark_id" + t.integer "user_id" + t.integer "rating" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "bookmarks", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "url" + t.text "title" + t.text "description" + t.integer "user_id" + t.integer "topic_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "courses", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| @@ -132,12 +156,37 @@ t.index ["to_id"], name: "fk_invitationto_users" end + create_table "join_team_requests", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "participant_id" + t.integer "team_id" + t.text "comments" + t.string "status" + end + + create_table "nodes", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.integer "parent_id" + t.integer "node_object_id" + t.string "type" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "participants", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "user_id" t.bigint "assignment_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "can_submit", default: true + t.boolean "can_review", default: true + t.string "handle" + t.boolean "permission_granted" + t.bigint "join_team_request_id" + t.bigint "team_id" t.index ["assignment_id"], name: "index_participants_on_assignment_id" + t.index ["join_team_request_id"], name: "index_participants_on_join_team_request_id" + t.index ["team_id"], name: "index_participants_on_team_id" t.index ["user_id"], name: "fk_participant_users" t.index ["user_id"], name: "index_participants_on_user_id" end @@ -165,10 +214,11 @@ t.boolean "break_before" t.string "max_label" t.string "min_label" - t.integer "questionnaire_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "questionnaire_id", null: false t.index ["questionnaire_id"], name: "fk_question_questionnaires" + t.index ["questionnaire_id"], name: "index_questions_on_questionnaire_id" end create_table "response_maps", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| @@ -198,6 +248,33 @@ t.index ["parent_id"], name: "fk_rails_4404228d2f" end + create_table "sign_up_topics", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "topic_name", null: false + t.bigint "assignment_id", null: false + t.integer "max_choosers", default: 0, null: false + t.text "category" + t.string "topic_identifier", limit: 10 + t.integer "micropayment", default: 0 + t.integer "private_to" + t.text "description" + t.string "link" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["assignment_id"], name: "fk_sign_up_categories_sign_up_topics" + t.index ["assignment_id"], name: "index_sign_up_topics_on_assignment_id" + end + + create_table "signed_up_teams", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "sign_up_topic_id", null: false + t.bigint "team_id", null: false + t.boolean "is_waitlisted" + t.integer "preference_priority_number" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["sign_up_topic_id"], name: "index_signed_up_teams_on_sign_up_topic_id" + t.index ["team_id"], name: "index_signed_up_teams_on_team_id" + end + create_table "ta_mappings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "course_id", null: false t.bigint "user_id", null: false @@ -211,6 +288,17 @@ create_table "teams", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "assignment_id", null: false + t.index ["assignment_id"], name: "index_teams_on_assignment_id" + end + + create_table "teams_users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "team_id", null: false + t.bigint "user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["team_id"], name: "index_teams_users_on_team_id" + t.index ["user_id"], name: "index_teams_users_on_user_id" end create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| @@ -242,13 +330,24 @@ add_foreign_key "account_requests", "institutions" add_foreign_key "account_requests", "roles" + add_foreign_key "assignments", "courses" + add_foreign_key "assignments", "users", column: "instructor_id" add_foreign_key "courses", "institutions" add_foreign_key "courses", "users", column: "instructor_id" add_foreign_key "participants", "assignments" + add_foreign_key "participants", "join_team_requests" + add_foreign_key "participants", "teams" add_foreign_key "participants", "users" + add_foreign_key "questions", "questionnaires" add_foreign_key "roles", "roles", column: "parent_id", on_delete: :cascade + add_foreign_key "sign_up_topics", "assignments" + add_foreign_key "signed_up_teams", "sign_up_topics" + add_foreign_key "signed_up_teams", "teams" add_foreign_key "ta_mappings", "courses" add_foreign_key "ta_mappings", "users" + add_foreign_key "teams", "assignments" + add_foreign_key "teams_users", "teams" + add_foreign_key "teams_users", "users" add_foreign_key "users", "institutions" add_foreign_key "users", "roles" add_foreign_key "users", "users", column: "parent_id" diff --git a/spec/factories.rb b/spec/factories.rb index 0a60e2478..85a4e092c 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,6 +1,14 @@ FactoryBot.define do + factory :join_team_request do - + end + + factory :bookmark do + url { "MyText" } + title { "MyText" } + description { "MyText" } + user_id { 1 } + topic_id { 1 } end factory :user do diff --git a/spec/requests/api/v1/bookmarks_controller_spec.rb b/spec/requests/api/v1/bookmarks_controller_spec.rb new file mode 100644 index 000000000..d9b0de2b9 --- /dev/null +++ b/spec/requests/api/v1/bookmarks_controller_spec.rb @@ -0,0 +1,279 @@ +require 'swagger_helper' + +# Rspec test for Bookmarks Controller +RSpec.describe 'api/v1/bookmarks', type: :request do + + path '/api/v1/bookmarks' do + + # Creation of dummy objects for the test with the help of let statements + let(:user) { User.create(email: 'test@test.com', password: 'password') } + let(:bookmark1) do + user + Bookmark.create( + url: 'http://example.com', + title: 'Example Bookmark', + description: 'An example bookmark', + topic_id: 1, + rating: 5, + user_id: user.id + ) + end + + let(:bookmark2) do + user + Bookmark.create( + url: 'http://example2.com', + title: 'Example Bookmark 2', + description: 'Another example bookmark', + topic_id: 2, + rating: 4, + user_id: user.id + ) + end + + # get request on /api/v1/bookmarks return list of bookmarks with response 200 + get('list bookmarks') do + tags 'Bookmarks' + produces 'application/json' + response(200, 'successful') do + run_test! do + expect(response.body.size).to eq(2) + end + end + end + + post('create bookmark') do + tags 'Bookmarks' + let(:user) { User.create(email: 'test@test.com', password: '123456') } + let(:valid_bookmark_params) do + { + url: 'http://example.com', + title: 'Example Bookmark', + description: 'An example bookmark', + topic_id: 1, + rating: 5 + } + end + + let(:invalid_bookmark_params) do + { + url: nil, # invalid url + title: 'Example Bookmark', + description: 'An example bookmark', + topic_id: 1, + rating: 5 + } + end + + consumes 'application/json' + produces 'application/json' + parameter name: :bookmark, in: :body, schema: { + type: :object, + properties: { + url: { type: :string }, + title: { type: :string }, + description: { type: :string }, + topic_id: { type: :integer }, + rating: { type: :integer } + }, + required: %w[url title description topic_id rating] + } + + # post request on /api/v1/bookmarks creates bookmark with response 201 when correct params are passed + response(201, 'created') do + let(:bookmark) do + user + Bookmark.create(valid_bookmark_params) + end + run_test! do + expect(response.body).to include('"title":"Example Bookmark"') + end + end + + # post request on /api/v1/bookmarks returns 422 response - unprocessable entity when wrong params is passed to create bookmark + response(422, 'unprocessable entity') do + let(:bookmark) do + user + Bookmark.create(invalid_bookmark_params) + end + run_test! + end + end + + end + + path '/api/v1/bookmarks/{id}' do + parameter name: 'id', in: :path, type: :integer + + # Creation of dummy objects for the test with the help of let statements + let(:user) { User.create(email: 'test@test.com', password: '123456') } + let(:valid_bookmark_params) do + { + url: 'http://example.com', + title: 'Example Bookmark', + description: 'An example bookmark', + topic_id: 1, + rating: 5, + user_id: user.id + } + end + + let(:bookmark) do + user + Bookmark.create(valid_bookmark_params) + end + + let(:id) do + bookmark + bookmark.id + end + + # Get request on /api/v1/bookmarks/{id} returns the response 200 succesful - bookmark with id = {id} when correct id is passed which is in the database + get('show bookmark') do + tags 'Bookmarks' + produces 'application/json' + response(200, 'successful') do + run_test! do + expect(response.body).to include('"title":"Example Bookmark"') + end + end + + # Get request on /api/v1/bookmarks/{id} returns the response 404 not found - bookmark with id = {id} when correct id is passed which is not present in the database + response(404, 'not_found') do + let(:id) { 'invalid' } + run_test! do + expect(response.body).to include("Couldn't find Bookmark") + end + end + end + + put('update bookmark') do + tags 'Bookmarks' + consumes 'application/json' + produces 'application/json' + + parameter name: :body_params, in: :body, schema: { + type: :object, + properties: { + title: { type: :string } + } + } + + # put request on /api/v1/bookmarks/{id} returns 200 response succesful when bookmark id is present in the database and correct valid params are passed + response(200, 'successful') do + let(:body_params) do + { + title: 'Updated Bookmark Title' + } + end + run_test! do + expect(response.body).to include('"title":"Updated Bookmark Title"') + end + end + + # put request on /api/v1/bookmarks/{id} returns 404 not found when id is not present in the database which bookmark needs to be updated + response(404, 'not found') do + let(:id) { 0 } + let(:body_params) do + { + title: 'Updated Bookmark Title' + } + end + run_test! do + expect(response.body).to include("Couldn't find Bookmark") + end + end + + # put request on /api/v1/bookmarks/{id} returns 422 response unprocessable entity when correct parameters for the bookmark to be updated are not passed + response(422, 'unprocessable entity') do + let(:body_params) do + { + title: nil + } + end + schema type: :string + run_test! do + expect(response.body).to_not include('"title":null') + end + end + end + + delete('delete bookmark') do + tags 'Bookmarks' + produces 'application/json' + # delete request on /api/v1/bookmarks/{id} returns 204 succesful response when bookmark with id present in the database is succesfully deleted + response(204, 'successful') do + run_test! do + expect(Bookmark.exists?(id)).to eq(false) + end + end + + # delete request on /api/v1/bookmarks/{id} returns 404 not found response when bookmark id is not present in the database + response(404, 'not found') do + let(:id) { 0 } + run_test! do + expect(response.body).to include("Couldn't find Bookmark") + end + end + end + end + + path '/api/v1/bookmarks/{id}/bookmarkratings' do + parameter name: 'id', in: :path, type: :integer + + let(:user) { User.create(email: 'test@test.com', password: '123456') } + let(:bookmark) do + user + Bookmark.create( + url: 'http://example.com', + title: 'Example Bookmark', + description: 'An example bookmark', + topic_id: 1, + rating: 5, + user_id: user.id + ) + end + + let(:id) { bookmark.id } + + post('save bookmark rating score') do + tags 'Bookmarks' + consumes 'application/json' + produces 'application/json' + + parameter name: :rating, in: :body, schema: { + type: :object, + properties: { + rating: { type: :integer } + }, + required: %w[rating] + } + + response(200, 'successful') do + let(:rating) { 4 } + run_test! do + expect(response.body).to include('"rating":4') + end + end + end + + get('get bookmark rating score') do + tags 'Bookmarks' + produces 'application/json' + + response(200, 'successful') do + let(:bookmark_rating) { BookmarkRating.create(bookmark_id: bookmark.id, user_id: user.id, rating: 5) } + run_test! do + expect(response.body).to include('"rating":5') + end + end + + response(404, 'not found') do + let(:id) { 0 } + run_test! do + expect(response.body).to include("Couldn't find Bookmark") + end + end + end + end +end diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb index 5e0e3c352..024a99d29 100644 --- a/spec/swagger_helper.rb +++ b/spec/swagger_helper.rb @@ -21,6 +21,18 @@ title: 'EXPERTIZA API V1', version: 'v1' }, + components: { + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT" + } + } + }, + security:[ + bearerAuth: [] + ], paths: {}, servers: [ { diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml new file mode 100644 index 000000000..b660f13f1 --- /dev/null +++ b/swagger/v1/swagger.yaml @@ -0,0 +1,1310 @@ +--- +openapi: 3.0.1 +info: + title: EXPERTIZA API V1 + version: v1 +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT +security: +- bearerAuth: [] +paths: + "/api/v1/account_requests/pending": + get: + summary: List all Pending Account Requests + tags: + - Account Requests + responses: + '200': + description: List Pending Account Requests + "/api/v1/account_requests/processed": + get: + summary: List all Processed Account Requests + tags: + - Account Requests + responses: + '200': + description: List Processed Account Requests + "/api/v1/account_requests": + post: + summary: Create Account Request + tags: + - Account Requests + parameters: [] + responses: + '201': + description: Create an Account Request whose email already exists in Users + table + '422': + description: Attempt to Create an Account Request whose username already + exists in Users table + requestBody: + content: + application/json: + schema: + type: object + properties: + username: + type: string + full_name: + type: string + email: + type: string + introduction: + type: string + role_id: + type: integer + institution_id: + type: integer + required: + - username + - full_name + - email + - introduction + - role_id + - institution_id + "/api/v1/account_requests/{id}": + parameters: + - name: id + in: path + description: id of the Account Request + required: true + schema: + type: integer + get: + summary: Show a specific Account Request by id + tags: + - Account Requests + responses: + '200': + description: Retrieve a specific account request with valid id + patch: + summary: Update Account Request + tags: + - Account Requests + parameters: [] + responses: + '200': + description: Reject account request + '422': + description: Attempt to send Invalid status in Patch + requestBody: + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: Approved + required: + - status + put: + summary: Update Account Request + tags: + - Account Requests + parameters: [] + responses: + '200': + description: Reject account request + '422': + description: Attempt to send Invalid status in PUT + requestBody: + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: Approved + required: + - status + delete: + summary: Delete Account Request + tags: + - Account Requests + responses: + '204': + description: successful + "/api/v1/bookmarks": + get: + summary: list bookmarks + tags: + - Bookmarks + responses: + '200': + description: successful + post: + summary: create bookmark + tags: + - Bookmarks + parameters: [] + responses: + '201': + description: created + '422': + description: unprocessable entity + requestBody: + content: + application/json: + schema: + type: object + properties: + url: + type: string + title: + type: string + description: + type: string + topic_id: + type: integer + rating: + type: integer + required: + - url + - title + - description + - topic_id + - rating + "/api/v1/bookmarks/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + get: + summary: show bookmark + tags: + - Bookmarks + responses: + '200': + description: successful + '404': + description: not_found + put: + summary: update bookmark + tags: + - Bookmarks + parameters: [] + responses: + '200': + description: successful + '404': + description: not found + '422': + description: unprocessable entity + content: + application/json: + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + title: + type: string + delete: + summary: delete bookmark + tags: + - Bookmarks + responses: + '204': + description: successful + '404': + description: not found + "/api/v1/bookmarks/{id}/bookmarkratings": + parameters: + - name: id + in: path + required: true + schema: + type: integer + post: + summary: save bookmark rating score + tags: + - Bookmarks + parameters: [] + responses: + '200': + description: successful + requestBody: + content: + application/json: + schema: + type: object + properties: + rating: + type: integer + required: + - rating + get: + summary: get bookmark rating score + tags: + - Bookmarks + responses: + '200': + description: successful + '404': + description: not found + "/api/v1/courses/{id}/add_ta/{ta_id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + - name: ta_id + in: path + required: true + schema: + type: integer + get: + summary: add_ta course + tags: + - Courses + responses: + '201': + description: successful + "/api/v1/courses/{id}/tas": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + get: + summary: view_tas course + tags: + - Courses + responses: + '200': + description: successful + "/api/v1/courses/{id}/remove_ta/{ta_id}": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + - name: ta_id + in: path + description: ta_id + required: true + schema: + type: string + get: + summary: remove_ta course + tags: + - Courses + responses: + '200': + description: successful + "/api/v1/courses/{id}/copy": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + get: + summary: copy course + tags: + - Courses + responses: + '200': + description: successful + "/api/v1/courses": + get: + summary: list courses + tags: + - Courses + responses: + '200': + description: successful + post: + summary: create course + tags: + - Courses + parameters: [] + responses: + '201': + description: successful + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + directory_path: + type: string + instructor_id: + type: integer + institution_id: + type: integer + info: + type: string + required: + - name + - directory_path + - institution_id + - instructor_id + "/api/v1/courses/{id}": + parameters: + - name: id + in: path + description: id + required: true + schema: + type: string + get: + summary: show course + tags: + - Courses + responses: + '200': + description: successful + patch: + summary: update course + tags: + - Courses + parameters: [] + responses: + '200': + description: successful + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + directory_path: + type: string + instructor_id: + type: integer + institution_id: + type: integer + info: + type: string + required: [] + put: + summary: update course + tags: + - Courses + parameters: [] + responses: + '200': + description: successful + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + directory_path: + type: string + instructor_id: + type: integer + institution_id: + type: integer + info: + type: string + required: [] + delete: + summary: delete course + tags: + - Courses + responses: + '204': + description: successful + "/api/v1/institutions": + get: + summary: list institutions + tags: + - Institutions + responses: + '200': + description: successful + post: + summary: create institution + tags: + - Institutions + parameters: [] + responses: + '201': + description: Created a institution + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + required: + - name + "/api/v1/institutions/{id}": + parameters: + - name: id + in: path + description: id of the institution + required: true + schema: + type: integer + get: + summary: show institution + tags: + - Institutions + responses: + '200': + description: successful + patch: + summary: update institution + tags: + - Institutions + parameters: [] + responses: + '200': + description: successful + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + required: + - name + put: + summary: update institution + tags: + - Institutions + parameters: [] + responses: + '200': + description: successful + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + required: + - name + delete: + summary: delete institution + tags: + - Institutions + responses: + '200': + description: successful + "/api/v1/invitations": + get: + summary: list invitations + tags: + - Invitations + responses: + '200': + description: Success + post: + summary: create invitation + tags: + - Invitations + parameters: [] + responses: + '201': + description: Create successful + '422': + description: Invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + assignment_id: + type: integer + from_id: + type: integer + to_id: + type: integer + reply_status: + type: string + required: + - assignment_id + - from_id + - to_id + "/api/v1/invitations/{id}": + parameters: + - name: id + in: path + description: id of the invitation + required: true + schema: + type: integer + get: + summary: show invitation + tags: + - Invitations + responses: + '200': + description: Show invitation + '404': + description: Not found + patch: + summary: update invitation + tags: + - Invitations + parameters: [] + responses: + '200': + description: Update successful + '422': + description: Invalid request + '404': + description: Not found + requestBody: + content: + application/json: + schema: + type: object + properties: + reply_status: + type: string + required: [] + delete: + summary: Delete invitation + tags: + - Invitations + responses: + '204': + description: Delete successful + '404': + description: Not found + "/api/v1/invitations/user/{user_id}/assignment/{assignment_id}": + parameters: + - name: user_id + in: path + description: id of user + required: true + schema: + type: integer + - name: assignment_id + in: path + description: id of assignment + required: true + schema: + type: integer + get: + summary: Show all invitation for the given user and assignment + tags: + - Invitations + responses: + '200': + description: Show all invitations for the user for an assignment + '404': + description: Not found + "/api/v1/questionnaires": + get: + summary: list questionnaires + tags: + - Questionnaires + responses: + '200': + description: successful + post: + summary: create questionnaire + tags: + - Questionnaires + parameters: [] + responses: + '201': + description: created + '422': + description: unprocessable entity + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + questionnaire_type: + type: string + private: + type: boolean + min_question_score: + type: integer + max_question_score: + type: integer + instructor_id: + type: integer + required: + - id + - name + - questionnaire_type + - private + - min_question_score + - max_question_score + - instructor_id + "/api/v1/questionnaires/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + get: + summary: show questionnaire + tags: + - Questionnaires + responses: + '200': + description: successful + '404': + description: not_found + put: + summary: update questionnaire + tags: + - Questionnaires + parameters: [] + responses: + '200': + description: successful + '404': + description: not found + '422': + description: unprocessable entity + content: + application/json: + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + min_question_score: + type: integer + patch: + summary: update questionnaire + tags: + - Questionnaires + parameters: [] + responses: + '200': + description: successful + '404': + description: not found + '422': + description: unprocessable entity + content: + application/json: + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + min_question_score: + type: integer + delete: + summary: delete questionnaire + tags: + - Questionnaires + responses: + '204': + description: successful + '404': + description: not found + "/api/v1/questionnaires/toggle_access/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + get: + summary: toggle access + tags: + - Questionnaires + responses: + '200': + description: successful + '404': + description: not found + "/api/v1/questionnaires/copy/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + post: + summary: copy questionnaire + tags: + - Questionnaires + responses: + '200': + description: successful + '404': + description: not found + "/api/v1/questions": + get: + summary: list questions + tags: + - Questions + responses: + '200': + description: successful + post: + summary: create question + tags: + - Questions + parameters: [] + responses: + '201': + description: created + '404': + description: questionnaire id not found + '422': + description: unprocessable entity + requestBody: + content: + application/json: + schema: + type: object + properties: + weight: + type: integer + questionnaire_id: + type: integer + break_before: + type: boolean + txt: + type: string + question_type: + type: string + required: + - weight + - questionnaire_id + - break_before + - txt + - question_type + "/api/v1/questions/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + get: + summary: show question + tags: + - Questions + responses: + '200': + description: successful + '404': + description: not_found + put: + summary: update question + tags: + - Questions + parameters: [] + responses: + '200': + description: successful + '404': + description: not found + '422': + description: unprocessable entity + content: + application/json: + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + break_before: + type: boolean + seq: + type: integer + patch: + summary: update question + tags: + - Questions + parameters: [] + responses: + '200': + description: successful + '404': + description: not found + '422': + description: unprocessable entity + content: + application/json: + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + break_before: + type: boolean + seq: + type: integer + delete: + summary: delete question + tags: + - Questions + responses: + '204': + description: successful + '404': + description: not found + "/api/v1/questions/delete_all/questionnaire/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + delete: + summary: delete all questions + tags: + - Questions + responses: + '200': + description: successful + '404': + description: not found + "/api/v1/questions/show_all/questionnaire/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + get: + summary: show all questions + tags: + - Questions + responses: + '200': + description: successful + '404': + description: not found + "/api/v1/questions/types": + get: + summary: question types + tags: + - Questions + responses: + '200': + description: successful + "/api/v1/roles": + get: + summary: list roles + tags: + - Roles + responses: + '200': + description: successful + post: + summary: create role + tags: + - Roles + parameters: [] + responses: + '201': + description: Created a role + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + parent_id: + type: integer + default_page_id: + type: integer + required: + - name + "/api/v1/roles/{id}": + parameters: + - name: id + in: path + description: id of the role + required: true + schema: + type: integer + get: + summary: show role + tags: + - Roles + responses: + '200': + description: successful + patch: + summary: update role + tags: + - Roles + parameters: [] + responses: + '200': + description: successful + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + parent_id: + type: integer + default_page_id: + type: integer + required: + - name + put: + summary: update role + tags: + - Roles + parameters: [] + responses: + '200': + description: successful + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + parent_id: + type: integer + default_page_id: + type: integer + required: + - name + delete: + summary: delete role + tags: + - Roles + responses: + '204': + description: successful + "/api/v1/sign_up_topics": + get: + summary: Get sign-up topics + parameters: + - name: assignment_id + in: query + description: Assignment ID + required: true + schema: + type: integer + - name: topic_ids + in: query + description: Topic Identifier + required: false + schema: + type: string + tags: + - SignUpTopic + responses: + '200': + description: successful + delete: + summary: Delete sign-up topics + parameters: + - name: assignment_id + in: query + description: Assignment ID + required: true + schema: + type: integer + - name: topic_ids + in: query + items: + type: string + description: Topic Identifiers to delete + required: false + schema: + type: array + tags: + - SignUpTopic + responses: + '200': + description: successful + post: + summary: create a new topic in the sheet + tags: + - SignUpTopic + parameters: [] + responses: + '201': + description: Success + requestBody: + content: + application/json: + schema: + type: object + properties: + topic_identifier: + type: integer + topic_name: + type: string + max_choosers: + type: integer + category: + type: string + assignment_id: + type: integer + micropayment: + type: integer + required: + - topic_identifier + - topic_name + - max_choosers + - category + - assignment_id + - micropayment + "/api/v1/sign_up_topics/{id}": + parameters: + - name: id + in: path + description: id of the sign up topic + required: true + schema: + type: integer + put: + summary: update a new topic in the sheet + tags: + - SignUpTopic + parameters: [] + responses: + '200': + description: successful + requestBody: + content: + application/json: + schema: + type: object + properties: + topic_identifier: + type: integer + topic_name: + type: string + max_choosers: + type: integer + category: + type: string + assignment_id: + type: integer + micropayment: + type: integer + required: + - topic_identifier + - topic_name + - category + - assignment_id + "/api/v1/signed_up_teams/sign_up": + post: + summary: Creates a signed up team + tags: + - SignedUpTeams + parameters: [] + responses: + '201': + description: signed up team created + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + team_id: + type: integer + topic_id: + type: integer + required: + - team_id + - topic_id + "/api/v1/signed_up_teams/sign_up_student": + parameters: + - name: user_id + in: query + description: User ID + required: true + schema: + type: integer + post: + summary: Creates a signed up team by student + tags: + - SignedUpTeams + parameters: [] + responses: + '201': + description: signed up team created + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + topic_id: + type: integer + required: + - topic_id + "/api/v1/signed_up_teams": + parameters: + - name: topic_id + in: query + description: Topic ID + required: true + schema: + type: integer + get: + summary: Retrieves signed up teams + tags: + - SignedUpTeams + responses: + '200': + description: signed up teams found + content: + application/json: + schema: + type: array + properties: + id: + type: integer + topic_id: + type: integer + team_id: + type: integer + is_waitlisted: + type: boolean + preference_priority_number: + type: integer + required: + - id + - topic_id + - team_id + - is_waitlisted + - preference_priority_number + '404': + description: signed up teams not found + "/api/v1/signed_up_teams/{id}": + parameters: + - name: id + in: path + required: true + schema: + type: integer + put: + summary: Updates a signed up team + tags: + - SignedUpTeams + parameters: [] + responses: + '200': + description: signed up team updated + '422': + description: invalid request + requestBody: + content: + application/json: + schema: + type: object + properties: + is_waitlisted: + type: boolean + preference_priority_number: + type: integer + delete: + summary: Deletes a signed up team + tags: + - SignedUpTeams + responses: + '204': + description: signed up team deleted + '422': + description: invalid request + "/login": + post: + summary: Logs in a user + tags: + - Authentication + parameters: [] + responses: + '200': + description: successful login + '401': + description: invalid credentials + requestBody: + content: + application/json: + schema: + type: object + properties: + user_name: + type: string + password: + type: string + required: + - user_name + - password +servers: +- url: http://{defaultHost} + variables: + defaultHost: + default: localhost:3002