diff --git a/.gitignore b/.gitignore index 0cb6eeb..8082097 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.gem /.bundle/ /.yardoc /Gemfile.lock diff --git a/README.md b/README.md index 0061c73..3819879 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,22 @@ **Important:** this gem is not production ready. -This is the code that orchestrates Zipmark's automatically generated -documentation and its publication. +## The problem + +You have systems that change and their documentation gets +outdated because they are maintaned manually. + +**The solution** + +DocOrchestra attempts to help mitigate that problem with several +functionalities. This is the roadmap + +* automate document generation (e.g [rspec_api_documentation](https://github.com/zipmark/rspec_api_documentation) ) +* automate linting of the document files (e.g [linter-api-blueprint](https://github.com/zdne/linter-api-blueprint) +* automate testing the documentation against real APIs (e.g [dredd](https://github.com/apiaryio/dredd)) +* automate concatenating multiple documentation files into one (e.g microservirces) +* automate generating [API Elements](http://api-elements.readthedocs.io/en/latest/) +* automate publishing the documentation to the cloud (e.g S3) ## Installation @@ -19,9 +33,14 @@ And then execute: ## Usage -Pending +Running this tool requires specifying a few options via ENV variables, like +the following: + +``` +DOC_PATH=./docs/main.apib DOC_STORAGE=s3 doc_orchestra sync +``` -## Development +See [Getting Started](/docs/getting_started.md) for details. ## Contributing diff --git a/bin/doc_orchestra b/bin/doc_orchestra new file mode 100755 index 0000000..4e9a07b --- /dev/null +++ b/bin/doc_orchestra @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby +require "bundler/setup" +require "doc_orchestra" + +DocOrchestra.run diff --git a/doc_orchestra.gemspec b/doc_orchestra.gemspec index c956cdf..316ae17 100644 --- a/doc_orchestra.gemspec +++ b/doc_orchestra.gemspec @@ -21,6 +21,8 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + spec.add_dependency "aws-sdk", "~> 2.6" + spec.add_development_dependency "bundler", "~> 1.13" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.5" diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 0000000..f3be9e3 --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,26 @@ +# Getting Started + +[WIP] + +Each one of these steps will depend on the orchestration strategies chosen. +These are the strategies that can be defined: + +* `DOC_FORMAT`: depending on this value, specific linting and testing tools + will be used. For instance, in a project with API Blueprint, + `DOC_FORMAT=apib` will define usage of `dredd` as testing tool. +* `DOC_STORAGE`: where will + will be used. For instance, in a project with API Blueprint, + `DOC_FORMAT=apib` will define usage of `dredd` as testing tool. + + +### Options + +These are the options that can be defined. For details on how + +* `DOC_FORMAT` (required, string): defines the format of the documentation. + Accepted values are: `apib` (APIBlueprint). +* `DOC_STORAGE` (required, string): defines where the documentation will be stored. + Accepted values are: `s3` (APIBlueprint). +* `DOC_ELEMENTS` (int): `1` to convert docs to + [API Elements](http://api-elements.readthedocs.io/en/latest/). Defaults to + `0`. diff --git a/lib/doc_orchestra.rb b/lib/doc_orchestra.rb index 5eec888..662ae03 100644 --- a/lib/doc_orchestra.rb +++ b/lib/doc_orchestra.rb @@ -1,4 +1,15 @@ require "doc_orchestra/version" +require "doc_orchestra/configuration" +require "doc_orchestra/log" +require "doc_orchestra/strategy" +require "doc_orchestra/strategies/storage" +require "doc_orchestra/strategies/storages/s3" module DocOrchestra + def self.run(env = ENV) + @config = Configuration.new(env) + + @strategy = Strategy.new(@config) + @strategy.orchestrate + end end diff --git a/lib/doc_orchestra/configuration.rb b/lib/doc_orchestra/configuration.rb new file mode 100644 index 0000000..c4355d9 --- /dev/null +++ b/lib/doc_orchestra/configuration.rb @@ -0,0 +1,42 @@ +module DocOrchestra + class Configuration + class UnexpectedConfiguration < StandardError; end + class MissingConfiguration < StandardError; end + + def initialize(env) + @env = env + @dictionary = {} + end + + def [](key) + if @env[key] != "" + @env[key] + end + end + + def required_keys(*keys) + keys.each do |key| + if blank_value?(@env[key]) + raise MissingConfiguration, "The following environment variables are missing: #{key}" + end + end + end + + def required_values(key, *values) + required_keys(key) + unless values.include?(self[key]) + raise UnexpectedConfiguration, "Environment variable #{key} accepted values are: #{values.join(", ")} (#{self[key]} provided). #{help_url}" + end + end + + private + + def blank_value?(str) + str.nil? || str == "" + end + + def help_url + "See https://github.com/zipmark/doc_orchestra for details." + end + end +end diff --git a/lib/doc_orchestra/log.rb b/lib/doc_orchestra/log.rb new file mode 100644 index 0000000..ca422c9 --- /dev/null +++ b/lib/doc_orchestra/log.rb @@ -0,0 +1,7 @@ +module DocOrchestra + class Log + def self.out(str) + puts str + end + end +end diff --git a/lib/doc_orchestra/strategies/format/apib.rb b/lib/doc_orchestra/strategies/format/apib.rb new file mode 100644 index 0000000..f1c1880 --- /dev/null +++ b/lib/doc_orchestra/strategies/format/apib.rb @@ -0,0 +1,19 @@ +module DocOrchestra + module Strategies + module FileFormat + class Apib + def initialize(options) + @options = options + end + + def method + + end + + private + + attr_reader :options + end + end + end +end diff --git a/lib/doc_orchestra/strategies/storage.rb b/lib/doc_orchestra/strategies/storage.rb new file mode 100644 index 0000000..89e02dc --- /dev/null +++ b/lib/doc_orchestra/strategies/storage.rb @@ -0,0 +1,7 @@ +module DocOrchestra + module Strategies + class Storage + + end + end +end diff --git a/lib/doc_orchestra/strategies/storages/s3.rb b/lib/doc_orchestra/strategies/storages/s3.rb new file mode 100644 index 0000000..9ff6f4b --- /dev/null +++ b/lib/doc_orchestra/strategies/storages/s3.rb @@ -0,0 +1,47 @@ +require 'aws-sdk' + +module DocOrchestra + module Strategies + module Storages + class S3 + REQUIRED_CONFIG = [ + "STORAGE_KEY", "STORAGE_SECRET", "STORAGE_BUCKET" + ] + + def initialize(config:, region: 'us-west-2') + config.required_keys(*REQUIRED_CONFIG) + @key = config["STORAGE_KEY"] + @secret = config["STORAGE_SECRET"] + @bucket = config["STORAGE_BUCKET"] + @region = config["STORAGE_REGION"] || region + end + + def upload + setup_credentials + create_bucket + end + + private + + def setup_credentials + Aws.config.update( + region: @region, + credentials: credentials + ) + end + + def credentials + Aws::Credentials.new(@key, @secret) + end + + def create_bucket + s3 = Aws::S3::Resource.new + s3.bucket(@bucket).create + Log.out("Creating bucket '#{@bucket}'... done.") + rescue Aws::S3::Errors::BucketAlreadyExists, Aws::S3::Errors::BucketAlreadyOwnedByYou + Log.out("Bucket '#{@bucket}' already exists.") + end + end + end + end +end diff --git a/lib/doc_orchestra/strategy.rb b/lib/doc_orchestra/strategy.rb new file mode 100644 index 0000000..86c9dde --- /dev/null +++ b/lib/doc_orchestra/strategy.rb @@ -0,0 +1,18 @@ +module DocOrchestra + class Strategy + def initialize(config) + config.required_values("DOC_STORAGE", "s3") + @config = config + end + + def orchestrate + storage.upload + end + + private + + def storage + Strategies::Storages::S3.new(config: @config) + end + end +end diff --git a/spec/doc_orchestra/configuration_spec.rb b/spec/doc_orchestra/configuration_spec.rb new file mode 100644 index 0000000..10ec7ee --- /dev/null +++ b/spec/doc_orchestra/configuration_spec.rb @@ -0,0 +1,77 @@ +require "spec_helper" + +describe DocOrchestra::Configuration do + let(:env) do + { + "DOC_STORAGE" => "s3", + "CUSTOM_KEY" => "custom value", + "BLANK_STRING" => "", + } + end + + subject(:config) { described_class.new(env) } + + describe "#required_keys" do + context 'when no key is missing' do + it 'does not raise errors' do + expect { + config.required_keys("CUSTOM_KEY", "DOC_STORAGE") + }.to_not raise_error + end + end + + context 'when one key is not defined' do + it 'raises an error' do + expect { + config.required_keys("MISSING_KEY", "DOC_STORAGE") + }.to raise_error described_class::MissingConfiguration + end + end + end + + describe '#[]' do + context 'value exists' do + it 'returns an env var value' do + expect(config["CUSTOM_KEY"]).to eq "custom value" + end + end + + context 'value is a blank string' do + it 'returns nil' do + expect(config["BLANK_STRING"]).to eq nil + end + end + + context 'value does not exist' do + it 'returns nil' do + expect(config["NO_KEY"]).to eq nil + end + end + end + + describe '#required_values' do + context 'a value is incorrect' do + it 'raises' do + expect { + config.required_values("DOC_STORAGE", "s4", "s5") + }.to raise_error described_class::UnexpectedConfiguration + end + end + + context 'the key does not exist' do + it 'raises' do + expect { + config.required_values("NO_KEY", "s3") + }.to raise_error described_class::MissingConfiguration + end + end + + context 'a value is incorrect' do + it 'raises' do + expect { + config.required_values("DOC_STORAGE", "s3") + }.to_not raise_error + end + end + end +end diff --git a/spec/doc_orchestra/strategies/storages/s3_spec.rb b/spec/doc_orchestra/strategies/storages/s3_spec.rb new file mode 100644 index 0000000..e94a4c5 --- /dev/null +++ b/spec/doc_orchestra/strategies/storages/s3_spec.rb @@ -0,0 +1,14 @@ +require "doc_orchestra/strategies/storages/s3" + +describe DocOrchestra::Strategies::Storages::S3 do + let(:item) { double } + + subject { described_class.new(item) } + + describe "#upload" do + it "returns true" do + subject.method + + end + end +end diff --git a/spec/fixtures/documents/main.apib b/spec/fixtures/documents/main.apib new file mode 100644 index 0000000..e3398df --- /dev/null +++ b/spec/fixtures/documents/main.apib @@ -0,0 +1 @@ +# My documentation diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3dbe855..f808696 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,4 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) require "doc_orchestra" +require 'pry' +require 'awesome_print'