-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…training-tips-a-user-has-completed [GT-1852] API support (the) tracking (of) training tips (that) a user has completed
- Loading branch information
Showing
12 changed files
with
294 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
class TrainingTipsController < WithUserController | ||
before_action :convert_hyphen_to_dash, only: [:create, :update] | ||
|
||
def create | ||
user_training_tip = @user.user_training_tips.create!(permitted_params) | ||
response.headers["Location"] = "users/#{@user.id}/training-tips/#{user_training_tip.id}" | ||
render json: user_training_tip, status: :created | ||
rescue ActiveRecord::RecordInvalid => e | ||
render json: {errors: formatted_errors("record_invalid", e)}, status: :unprocessable_entity | ||
end | ||
|
||
def update | ||
user_training_tip = @user.user_training_tips.find(params[:id]) | ||
user_training_tip.update!(permitted_params) | ||
response.headers["Location"] = "users/me/training-tips/#{user_training_tip.id}" | ||
render json: user_training_tip | ||
end | ||
|
||
def destroy | ||
user_training_tip = @user.user_training_tips.find(params[:id]) | ||
user_training_tip.destroy! | ||
head :no_content | ||
end | ||
|
||
protected | ||
|
||
def permitted_params | ||
tool_id = params.dig(:data, :relationships, :tool, :data, :id).to_i | ||
language_id = params.dig(:data, :relationships, :language, :data, :id).to_i | ||
permit_params(:tip_id, :is_completed).merge(tool_id: tool_id, language_id: language_id) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
class UserTrainingTip < ActiveRecord::Base | ||
validates :tool_id, uniqueness: {scope: [:user_id, :tool_id, :language_id, :tip_id], message: "combination already exists"} | ||
validates :tip_id, presence: true | ||
|
||
belongs_to :user | ||
belongs_to :language | ||
belongs_to :tool, class_name: "Resource" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# frozen_string_literal: true | ||
|
||
class UserTrainingTipSerializer < ActiveModel::Serializer | ||
attribute :tip_id, key: "tip-id" | ||
attribute :is_completed, key: "is-completed" | ||
|
||
type "training-tip" | ||
|
||
belongs_to :language | ||
belongs_to :tool | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
class CreateUserTrainingTip < ActiveRecord::Migration[6.1] | ||
def change | ||
create_table :user_training_tips do |t| | ||
t.references :user, null: false, foreign_key: true | ||
t.references :tool, null: false, foreign_key: {to_table: :resources} | ||
t.references :language, null: false, foreign_key: true | ||
t.string :tip_id | ||
t.boolean :is_completed | ||
|
||
t.timestamps | ||
end | ||
|
||
add_index :user_training_tips, [:user_id, :tool_id, :language_id, :tip_id], unique: true, name: "training-tips-unique-index" | ||
end | ||
end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
# frozen_string_literal: true | ||
|
||
require "acceptance_helper" | ||
|
||
resource "UserTrainingTips" do | ||
header "Accept", "application/vnd.api+json" | ||
header "Content-Type", "application/vnd.api+json" | ||
|
||
let!(:user) { FactoryBot.create(:user) } | ||
let(:raw_post) { params.to_json } | ||
let(:authorization) { AuthToken.generic_token } | ||
|
||
post "users/me/training-tips" do | ||
let(:attributes) do | ||
{ | ||
"tip-id": "tip id here", | ||
"is-completed": true | ||
} | ||
end | ||
|
||
let(:attributes_invalid) do | ||
{ | ||
"tip-id": "" | ||
} | ||
end | ||
|
||
let(:relationships) { | ||
{ | ||
language: { | ||
data: { | ||
type: "language", | ||
id: Language.first.id | ||
} | ||
}, | ||
|
||
tool: { | ||
data: { | ||
type: "resource", | ||
id: Resource.first.id | ||
} | ||
} | ||
} | ||
} | ||
|
||
requires_okta_login | ||
|
||
it "create user training tip" do | ||
do_request data: {type: "training-tip", attributes: attributes, relationships: relationships} | ||
|
||
expect(status).to eq(201) | ||
data = JSON.parse(response_body)["data"] | ||
expect(data).not_to be_nil | ||
expect(data["attributes"]).to eq(serializer_output_style(attributes)) | ||
expect(data["relationships"]).to eq(serializer_output_style(relationships)) | ||
end | ||
|
||
it "returns error message when user training tip is not created" do | ||
do_request data: {type: "training-tips", attributes: attributes_invalid, relationships: relationships} | ||
|
||
expect(status).to eq(400) | ||
expect(JSON.parse(response_body)["errors"]).not_to be_empty | ||
expect(JSON.parse(response_body)["errors"][0]["detail"]).to eql "Validation failed: Tip can't be blank" | ||
end | ||
end | ||
|
||
put "users/me/training-tips/:id" do | ||
requires_okta_login | ||
|
||
let(:tool) { Resource.first } | ||
let(:language) { Language.first } | ||
let(:training_tip) { FactoryBot.create(:user_training_tip, user_id: user.id, tool_id: tool.id, language_id: language.id, tip_id: "tip", is_completed: false) } | ||
let(:id) { training_tip.id } | ||
|
||
let(:attributes) do | ||
{ | ||
"tip-id": "new tip id", | ||
"is-completed": true | ||
} | ||
end | ||
|
||
let(:relationships) do | ||
{ | ||
language: { | ||
data: { | ||
type: "language", | ||
id: Language.second.id | ||
} | ||
}, | ||
|
||
tool: { | ||
data: { | ||
type: "resource", | ||
id: Resource.second.id | ||
} | ||
} | ||
} | ||
end | ||
|
||
it "updates a user training tip" do | ||
do_request id: training_tip.id, data: { | ||
type: "training-tip", | ||
attributes: attributes, | ||
relationships: relationships | ||
} | ||
|
||
expect(status).to eq(200) | ||
data = JSON.parse(response_body)["data"] | ||
expect(data).not_to be_nil | ||
expect(data["attributes"]).to eq(serializer_output_style(attributes)) | ||
expect(data["relationships"]).to eq(serializer_output_style(relationships)) | ||
end | ||
end | ||
|
||
delete "users/me/training-tips/:id" do | ||
requires_okta_login | ||
|
||
let(:resource) { Resource.first } | ||
let(:tool) { Resource.first } | ||
let(:language) { Language.first } | ||
let!(:training_tip) { FactoryBot.create(:user_training_tip, user_id: user.id, tool_id: tool.id, language_id: language.id, tip_id: "tip", is_completed: false) } | ||
let(:id) { training_tip.id } | ||
let(:invalid_id) { -1 } | ||
requires_authorization | ||
|
||
it "delete user training tip succeed and returns ':not_content'" do | ||
expect do | ||
do_request id: id | ||
end.to change(UserTrainingTip, :count).by(-1) | ||
|
||
expect(status).to be(204) | ||
expect(UserTrainingTip.find_by(id: id)).to be_nil | ||
end | ||
|
||
it "delete user training tip fails and returns ':not_found'" do | ||
do_request id: invalid_id | ||
|
||
expect(status).to be(404) | ||
end | ||
end | ||
|
||
get "users/me?include=training-tips" do | ||
requires_authorization | ||
|
||
let(:tool) { Resource.first } | ||
let(:language) { Language.first } | ||
let!(:training_tip_1) { FactoryBot.create(:user_training_tip, user_id: user.id, tool_id: tool.id, language_id: language.id, tip_id: "tip", is_completed: false) } | ||
|
||
let(:tool_2) { Resource.second } | ||
let(:language_2) { Language.second } | ||
let!(:training_tip_2) { FactoryBot.create(:user_training_tip, user_id: user.id, tool_id: tool_2.id, language_id: language_2.id, tip_id: "tip", is_completed: false) } | ||
|
||
# this should not be included | ||
let(:other_user) { FactoryBot.create(:user) } | ||
let(:tool_3) { Resource.first } | ||
let(:language_3) { Language.first } | ||
let!(:training_tip_3) { FactoryBot.create(:user_training_tip, user_id: other_user.id, tool_id: tool_3.id, language_id: language_3.id, tip_id: "tip", is_completed: false) } | ||
|
||
it "gets all training tips for a user" do | ||
do_request | ||
expect(status).to eq(200) | ||
|
||
data = JSON.parse(response_body)["data"] | ||
expect(data["relationships"]["training-tips"]["data"]).to eq([{"id" => training_tip_1.id.to_s, "type" => "training-tip"}, {"id" => training_tip_2.id.to_s, "type" => "training-tip"}]) | ||
|
||
included = JSON.parse(response_body)["included"] | ||
expect(included.first["id"]).to eq(training_tip_1.id.to_s) | ||
expect(included.second["id"]).to eq(training_tip_2.id.to_s) | ||
end | ||
end | ||
|
||
# change _ to - in keys, and make any ids (number values) strings | ||
def serializer_output_style(hash) | ||
hash.deep_transform_keys { |key| key.to_s.tr("_", "-") }.deep_transform_values { |v| /^\d+$/.match?(v.to_s) ? v.to_s : v } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
FactoryBot.define do | ||
factory :user_training_tip do | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# frozen_string_literal: true | ||
|
||
require "rails_helper" | ||
|
||
RSpec.describe UserTrainingTip, type: :model do | ||
context "create new training tip" do | ||
let(:tip_id) { "tip" } | ||
let(:user) { FactoryBot.create(:user) } | ||
let(:tool) { Resource.first } | ||
let(:language) { Language.first } | ||
|
||
subject { UserTrainingTip.new(tool_id: tool.id, language_id: language.id, tip_id: tip_id, is_completed: true, user: user) } | ||
|
||
it "is valid" do | ||
expect(subject).to be_valid | ||
end | ||
end | ||
end |