Module: MinimumTerm::Conversion

Defined in:
lib/minimum-term/conversion.rb,
lib/minimum-term/conversion/error.rb,
lib/minimum-term/conversion/data_structure.rb,
lib/minimum-term/conversion/apiary_to_json_schema.rb

Defined Under Namespace

Classes: ApiaryToJsonSchema, DataStructure, Error

Class Method Summary collapse

Class Method Details

.data_structures_from_blueprint_ast(filename) ⇒ Object



103
104
105
106
107
# File 'lib/minimum-term/conversion.rb', line 103

def self.data_structures_from_blueprint_ast(filename)
  c = JSON.parse(open(filename).read)['content'].first
  return [] unless c
  c['content']
end

.mson_to_ast_json(filename) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/minimum-term/conversion.rb', line 109

def self.mson_to_ast_json(filename)
  input = filename
  output = filename.gsub(/\.\w+$/, '.blueprint-ast.json')

  cmd = "drafter -u -f json -o #{Shellwords.escape(output)} #{Shellwords.escape(input)}"
  stdin, stdout, status = Open3.capture3(cmd)

  {
    cmd: cmd,
    outfile: output,
    stdin: stdin,
    stdout: stdout,
    status: status
  }
end

.mson_to_json_schema(filename:, keep_intermediary_files: false, verbose: false) ⇒ Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/minimum-term/conversion.rb', line 8

def self.mson_to_json_schema(filename:, keep_intermediary_files: false, verbose: false)
  begin
    mson_to_json_schema!(filename: filename, keep_intermediary_files: keep_intermediary_files, verbose: verbose)
    puts "OK ".green + filename if verbose
    true
  rescue
    puts "ERROR ".red + filename if verbose
    false
  end
end

.mson_to_json_schema!(filename:, keep_intermediary_files: false, verbose: true) ⇒ Object

Raises:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/minimum-term/conversion.rb', line 19

def self.mson_to_json_schema!(filename:, keep_intermediary_files: false, verbose: true)

  # For now, we'll use the containing directory's name as a scope
  service_scope = File.dirname(filename).split(File::SEPARATOR).last.underscore

  # Parse MSON to an apiary blueprint AST
  # (see https://github.com/apiaryio/api-blueprint/wiki/API-Blueprint-Map)
  to_ast = mson_to_ast_json(filename)
  raise Error, "Error: #{to_ast}" unless to_ast[:status] == 0

  # Pluck out Data structures from it
  data_structures = data_structures_from_blueprint_ast(to_ast[:outfile])

  # Generate json schema from each contained data structure
  schema = {
    "$schema"     => "http://json-schema.org/draft-04/schema#",
    "title"       => service_scope,
    "definitions" => {},
    "type"        => "object",
    "properties"  => {},
  }

  # The scope for the data structure is the name of the service
  # publishing the object. So if we're parsing a 'consume' schema,
  # the containing objects are alredy scoped (because a consume
  # schema says 'i consume object X from service Y'.
  basename = File.basename(filename)
  if basename.end_with?("publish.mson")
    data_structure_autoscope = service_scope
  elsif basename.end_with?("consume.mson")
    data_structure_autoscope = nil
  else
    raise Error, "Invalid filename #{basename}, can't tell if it's a publish or consume schema"
  end

  # The json schema we're constructing contains every known
  # object type in the 'definitions'. So if we have definitions for
  # the objects User, Post and Tag, the schema will look like this:
  #
  # {
  #   "$schema": "..."
  #
  #   "definitions": {
  #     "user": { "type": "object", "properties": { ... }}
  #     "post": { "type": "object", "properties": { ... }}
  #     "tag":  { "type": "object", "properties": { ... }}
  #   }
  #
  #   "properties": {
  #     "user": "#/definitions/user"
  #     "post": "#/definitions/post"
  #     "tag":  "#/definitions/tag"
  #   }
  #
  # }
  #
  # So when testing an object of type `user` against this schema,
  # we need to wrap it as:
  #
  # {
  #   user: {
  #     "your": "actual",
  #     "data": "goes here"
  #     }
  # }
  #
  data_structures.each do |data|
    schema_data = data['content'].select{|d| d['element'] == 'object' }.first
    id = schema_data['meta']['id']
    json= DataStructure.new(id, schema_data, data_structure_autoscope).to_json
    member = json.delete('title')
    schema['definitions'][member] = json
    schema['properties'][member] = {"$ref" => "#/definitions/#{member}"}
  end

  # Write it in a file
  outfile = filename.gsub(/\.\w+$/, '.schema.json')
  File.open(outfile, 'w'){ |f| f.puts JSON.pretty_generate(schema) }

  # Clean up
  FileUtils.rm_f(to_ast[:outfile]) unless keep_intermediary_files
  true
end