Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1b0fb11
api verification completed and initial user interface added
amythetester Mar 19, 2019
d3fdbef
added list and get methods to User and Channel classes
alexandria7 Mar 19, 2019
3f7eafc
implemented self.list for User and Channel in slack.rb file
alexandria7 Mar 19, 2019
fc5769e
added quit functionality and reprompt loop to slack.rb file
amythetester Mar 19, 2019
0e2c0fa
updated test_helper file to include VCR configuration
alexandria7 Mar 20, 2019
607d41a
implemented helper method to hold API information and parse JSON
amythetester Mar 20, 2019
4b89f88
implemented helper method for user.rb to store API info and parse thr…
alexandria7 Mar 20, 2019
65f53bb
wrote corresponding tests for 'list channels'
alexandria7 Mar 20, 2019
3e7ac4a
wrote corresponding tests for list users
amythetester Mar 20, 2019
05bee27
added Workspace class and select user and select channel methods
alexandria7 Mar 20, 2019
6498e56
added show_details method and functionality for workspace
alexandria7 Mar 21, 2019
c4697ce
edited cli formatting and updated self.details method to respond when…
alexandria7 Mar 21, 2019
e3e34f4
added test outlines for workspace
alexandria7 Mar 21, 2019
c267404
added preliminary code for sending messages to recipients
alexandria7 Mar 21, 2019
5e5665e
edited workspace to initialize with all channels and users
alexandria7 Mar 21, 2019
f58f4f2
changed class methods to instance methods in workspace.rb
amythetester Mar 21, 2019
5d541ed
fixed VCR boo-boo (moved initialization of Workspace into VCR.use_cas…
alexandria7 Mar 21, 2019
3b9e604
select_user tests added
amythetester Mar 21, 2019
d63ce76
added tests for show_details for workspace class
alexandria7 Mar 21, 2019
4379d22
added more tests for each workspace method
alexandria7 Mar 21, 2019
1af0227
edited UI for cleaner output
alexandria7 Mar 21, 2019
960edc3
edited and completed send_message functionality with corresponding tests
alexandria7 Mar 22, 2019
2d6a4f5
added tests for listing users and channels
amythetester Mar 22, 2019
e429dfc
added tests to user and channel. updated CLI to get rid of extra puts
amythetester Mar 22, 2019
8e24b97
added API testing
amythetester Mar 22, 2019
20a9e5f
project file clean up
amythetester Mar 22, 2019
4f0b1b9
additional project clean up
amythetester Mar 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions lib/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "dotenv"
require "httparty"

Dotenv.load

module SlackApi
class SlackError < StandardError; end

class Channel
url = "https://slack.com/api/channels.list"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make these constants instead of just regular variables.

key = ENV["SLACK_API_TOKEN"]

def self.channel_api(url, key)
parameters = { 'token': key }
response = HTTParty.get(url, query: parameters).to_s
response = JSON.parse(response)

if response["ok"] == true
return response["channels"]
else
raise SlackApi::SlackError, "Error with Channel API: #{response["error"]}"
end
end

def self.list(channels_list)
puts "\nHere are a list of your channels for this Workspace:"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should try to separate the "display" functions of your app with the functionality and business logic. This "model-like" class shouldn't have to worry about printing things to the screen, instead it should work with the api and return data back.

list_of_channels = channels_list.map do |channel|
"\nChannel name: #{channel["name"]}\nSlack ID: #{channel["id"]}\nTopic: #{channel["topic"]["value"]}\nMember count: #{channel["num_members"]}\n"
end
return list_of_channels
end
end
end
48 changes: 46 additions & 2 deletions lib/slack.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,55 @@
#!/usr/bin/env ruby

require_relative "user"
require_relative "channel"
require_relative "workspace"

Dotenv.load

def main
workspace = SlackApi::Workspace.new

user_options = "list users\nlist channels\nselect user\nselect channel\ndetails\nsend message\nquit"

puts "Welcome to the Ada Slack CLI!"
puts "\nThere are #{workspace.channels.length} channels and #{workspace.users.length} users in this Workspace."
puts "\nWhat would you like to do?:"
puts user_options

user_selection = gets.chomp
until user_selection == "quit"
case user_selection
when "list users"
puts SlackApi::User.list(workspace.users)
when "list channels"
puts SlackApi::Channel.list(workspace.channels)
when "select user"
print "\nEnter username or user's ID: "
user_input = gets.chomp
if workspace.select_user(user_input) != true
puts "\n~That user does not exist~"
end
when "select channel"
print "\nEnter channel name or channel's ID: "
user_input = gets.chomp
if workspace.select_channel(user_input) != true
puts "\n~That channel does not exist~"
end
when "details"
puts workspace.show_details
when "send message"
puts "\n(This will send to the recipient you have selected)"
print "What message would you like to send? "
user_message = gets.chomp
workspace.send_message(user_message)
end

# TODO project
puts "\nWhat would you like to do next?"
puts user_options
user_selection = gets.chomp
end

puts "Thank you for using the Ada Slack CLI"
end

main if __FILE__ == $PROGRAM_NAME
main if __FILE__ == $PROGRAM_NAME
33 changes: 33 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "dotenv"
require "httparty"

Dotenv.load

module SlackApi
class SlackError < StandardError; end

class User

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is just a collection of class methods, so you don't actually need a class, a module would work as well.

I do think a class makes sense, you can create instances of User with fields for name, id, etc.

url = "https://slack.com/api/users.list"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, you should default to using constants unless you want to change the values of these variables.

key = ENV["SLACK_API_TOKEN"]

def self.user_api(url, key)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method name could use a bit of work, rather than call it user_api I would call it, get_users. Also if key is already saved above, why do you need it as an argument?

parameters = { 'token': key }
response = HTTParty.get(url, query: parameters).to_s
response = JSON.parse(response)

if response["ok"] == true
return response["members"]
else
raise SlackApi::SlackError, "Error with User API: #{response["error"]}"
end
end

def self.list(users_list)
puts "\nHere are a list of your users for this Workspace:"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I would separate out the presentation with the business logic of your application.

list_of_users = users_list.map do |user|
"\nUsername: #{user["name"]}\nSlack ID: #{user["id"]}\nReal name: #{user["real_name"]}"
end
return list_of_users
end
end
end
81 changes: 81 additions & 0 deletions lib/workspace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require "dotenv"
require "httparty"

require_relative "channel"
require_relative "user"

Dotenv.load

module SlackApi
class SlackError < StandardError; end

class Workspace
attr_reader :channels, :users

def initialize
@users = SlackApi::User.user_api("https://slack.com/api/users.list", ENV["SLACK_API_TOKEN"])
@channels = SlackApi::Channel.channel_api("https://slack.com/api/channels.list", ENV["SLACK_API_TOKEN"])
@selected = ""
end

def select_channel(user_input)
@channels.each do |channel|

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also use the .find method as well.

if channel["name"] == user_input || channel["id"] == user_input
@selected = channel
return true
end
end
return false
end

def select_user(user_input)
@users.each do |user|
if user["name"] == user_input || user["id"] == user_input
@selected = user
return true
end
end
return false
end

def show_details
if @channels.include?(@selected)
channel_details = "\nChannel name: #{@selected["name"]}\nSlack ID: #{@selected["id"]}\nTopic: #{@selected["topic"]["value"]}\nMember count: #{@selected["num_members"]}"

return channel_details
elsif @users.include?(@selected)
user_details = "\nUsername: #{@selected["name"]}\nSlack ID: #{@selected["id"]} \nReal name: #{@selected["real_name"]}"

return user_details
else
error_message = "\n~You have not selected a user or channel yet.~"
return error_message
end
end

def send_message(message)
url = "https://slack.com/api/chat.postMessage"
key = ENV["SLACK_API_TOKEN"]

if @channels.include?(@selected) || @users.include?(@selected)
response = HTTParty.post(
url,
headers: { "Content-Type" => "application/x-www-form-urlencoded" },
body: {
token: key,
text: message,
channel: @selected["id"],
},
)
if response["ok"]
return true
else
raise SlackApi::SlackError, "Error when posting message to #{@selected["name"]}, error: #{response["error"]}"
end
else
error_message = "\n~You have not selected a user or channel yet.~"
return error_message
end
end
end
end
90 changes: 90 additions & 0 deletions specs/channel_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
require_relative "test_helper"

describe SlackApi::Channel do
describe "json channels" do
it "returns valid channels" do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

channels_list.each do |channel|
expect(channel["is_channel"]).must_equal true
end
end
end

it "return includes a specific channel" do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

expect(channels_list.first["name"]).must_equal "general"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different Slack orgs will return channels in different orders. I would instead try to verify that the list of channels contains "general" instead.

You can't make assumptions on the order that the API returns.

end
end

it "return includes a specific channel" do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

expect(channels_list.first["id"]).must_equal "CH2RC3CNQ"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're using this in several places, I would make the channel id a constant.

end
end

it "return includes a specific channel" do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

expect(channels_list.first["topic"]["value"]).must_equal "Company-wide announcements and work-based matters"
end
end

it "return includes a specific channel" do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

expect(channels_list.first["num_members"]).must_equal 2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a slack app will constantly change the number of users, I would instead just verify that the field exists and is a number.

You are also reusing the same test name in several different tests.

end
end

it "channels list will only include existent channels " do
VCR.use_cassette("slack_channels_json") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

channels_list.each do |channel|
name = "Not a Channel"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to write a test to verify that a channel that does not exist doesn't appear in the list.

expect(channel["name"]).wont_equal name
end
end
end

it "raises error when a bad API call is made" do
VCR.use_cassette("slack_channels_list") do
url = "https://slack.com/api/channels.list"

expect { SlackApi::Channel.channel_api(url, "") }.must_raise SlackApi::SlackError
end
end
end

describe "list channels" do
it "returns an array" do
VCR.use_cassette("slack_channels_list") do
url = "https://slack.com/api/channels.list"
key = ENV["SLACK_API_TOKEN"]
channels_list = SlackApi::Channel.channel_api(url, key)

expect(SlackApi::Channel.list(channels_list)).must_be_instance_of Array
end
end
end
end
33 changes: 25 additions & 8 deletions specs/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
require 'simplecov'
SimpleCov.start
require "simplecov"
SimpleCov.start do
add_filter %r{^/specs?/}
end

require 'minitest'
require 'minitest/autorun'
require 'minitest/reporters'
require 'minitest/skip_dsl'
require 'vcr'
require "dotenv"
Dotenv.load

require "minitest"
require "minitest/autorun"
require "minitest/reporters"
require "minitest/skip_dsl"
require "webmock/minitest"
require "vcr"

require_relative "../lib/channel"
require_relative "../lib/user"
require_relative "../lib/workspace"

Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new

VCR.configure do |config|
config.cassette_library_dir = "specs/cassettes"
config.hook_into :webmock
end
config.default_cassette_options = {
record: :new_episodes,
match_requests_on: [:method, :uri, :body],
}
config.filter_sensitive_data("SLACK_API_TOKEN") do
ENV["SLACK_API_TOKEN"]
end
end
Loading