Last active
June 11, 2019 23:56
-
-
Save MikaelSmith/ce008b2d2d4a00c1f20105fe7d5d6855 to your computer and use it in GitHub Desktop.
Wash External Plugin Example
This file contains hidden or 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
#!/usr/bin/env ruby | |
# A script for exploring your Goodreads library with Wash | |
# | |
# Requirements: | |
# - `gem install oauth nokogiri --user-install` | |
# Note that BitBar runs without shell setup, so it will likely use the system ruby. | |
# - A developer key/secret from https://www.goodreads.com/api/keys | |
# stored in GOODREADS_KEY, GOODREADS_SECRET environment variables. | |
require 'json' | |
require 'oauth' | |
require 'nokogiri' | |
# Step 1: figure out authentication | |
consumer = OAuth::Consumer.new(ENV['GOODREADS_KEY'], | |
ENV['GOODREADS_SECRET'], | |
site: 'https://www.goodreads.com') | |
# If we don't have an access token, request one. | |
# TODO: module-specific configuration of where to cache it. | |
# Will need to move this to init, and pass through as plugin state. | |
TOKEN_CACHE = File.join(Dir.home, '.puppetlabs', 'wash', 'goodreads.token') | |
begin | |
token_text = File.read(TOKEN_CACHE) | |
obj = JSON.parse(token_text) | |
@access_token = OAuth::AccessToken.new(consumer, obj['token'], obj['secret']) | |
rescue StandardError | |
request_token = consumer.get_request_token | |
puts "Please authorize at #{request_token.authorize_url}, then press enter once done" | |
gets | |
@access_token = request_token.get_access_token | |
File.write(TOKEN_CACHE, { token: @access_token.token, secret: @access_token.secret }.to_json) | |
end | |
# Returns an XML response body on OK status, else raises an error | |
def get(path) | |
response = @access_token.get(path) | |
if response.code_type != Net::HTTPOK | |
raise "#{response.code}: #{response.body}" | |
end | |
return Nokogiri::XML(response.body) | |
end | |
# Converts an XML book format from the goodreads API to a json representation of a book | |
def book_to_json(doc) | |
result = {} | |
doc.elements.each do |node| | |
unless node.text.empty? | |
result[node.name] = node.text | |
end | |
# TODO: handle things with more structure | |
# Handle HTML text: https://stackoverflow.com/questions/2505104/html-to-plain-text-with-ruby | |
end | |
return result | |
end | |
method, *rest = ARGV | |
case method | |
when 'init' | |
# Step 2: provide a basic init method | |
doc = get('/api/auth_user') | |
userid = doc.at('user').attribute('id') | |
puts ({ name: 'goodreads', methods: ['list'], state: JSON.dump(userid: userid) }).to_json | |
when 'list' | |
path, state = rest | |
state = JSON.parse(state) unless state.nil? || state.empty? | |
children = [] | |
if path == '/goodreads' | |
# Step 3: list the plugin root | |
doc = get('/shelf/list.xml') | |
doc.search('shelves user_shelf').each do |bookshelf| | |
name = bookshelf.at('name').content | |
meta = {} | |
bookshelf.elements.each do |node| | |
next if node.name == 'name' | |
meta[node.name] = node.content | |
end | |
# Step 4: add state we can use to keep track of what we're interacting with. TODO: use Oj for object serialization? | |
count = bookshelf.at('book_count').content.to_i | |
shelf = { type: 'shelf', name: name, userid: state['userid'], count: count } | |
children << { name: name, methods: ['list'], attributes: {meta: meta}, state: JSON.dump(shelf) } | |
end | |
elsif state['type'] == 'shelf' && state['count'] > 0 | |
# Step 5: list books on a shelf. TODO: Iterate over paginated results based on shelf count. | |
doc = get("/review/list/#{state['userid']}.xml?v=2&shelf=#{state['name']}&per_page=10") | |
doc.search('reviews review').each do |review| | |
name = review.at('book title').content | |
meta = book_to_json(review.at('book')) | |
children << { name: name, methods: [], attributes: {meta: meta} } | |
end | |
end | |
puts children.sort_by {|obj| obj['name']}.to_json | |
else | |
STDERR.puts "Unknown method invoked" | |
exit 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Docs at https://puppetlabs.github.io/wash/docs/external_plugins.