generator.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. from __future__ import absolute_import
  2. import os
  3. import zlib
  4. import json
  5. import click
  6. import urlparse
  7. import logging
  8. from datetime import datetime
  9. from subprocess import Popen, PIPE
  10. from contextlib import contextmanager
  11. HERE = os.path.abspath(os.path.dirname(__file__))
  12. SENTRY_CONFIG = os.path.join(HERE, 'sentry.conf.py')
  13. # No sentry or django imports before that point
  14. from sentry.utils import runner
  15. runner.configure(config_path=SENTRY_CONFIG, skip_backend_validation=True)
  16. from django.conf import settings
  17. # Fair game from here
  18. from django.core.management import call_command
  19. from sentry.utils.apidocs import Runner, MockUtils, iter_scenarios, \
  20. iter_endpoints, get_sections
  21. OUTPUT_PATH = os.path.join(HERE, 'cache')
  22. HOST = urlparse.urlparse(settings.SENTRY_URL_PREFIX).netloc
  23. # We don't care about you, go away
  24. _logger = logging.getLogger('sentry.events')
  25. _logger.disabled = True
  26. def color_for_string(s):
  27. colors = ('red', 'green', 'yellow', 'blue', 'cyan', 'magenta')
  28. return colors[zlib.crc32(s) % len(colors)]
  29. def report(category, message, fg=None):
  30. if fg is None:
  31. fg = color_for_string(category)
  32. click.echo('[%s] %s: %s' % (
  33. str(datetime.utcnow()).split('.')[0],
  34. click.style(category, fg=fg),
  35. message
  36. ))
  37. def launch_redis():
  38. report('redis', 'Launching redis server')
  39. cl = Popen(['redis-server', '-'], stdin=PIPE, stdout=open(os.devnull, 'r+'))
  40. cl.stdin.write('''
  41. port %(port)s
  42. databases %(databases)d
  43. save ""
  44. ''' % {
  45. 'port': str(settings.SENTRY_APIDOCS_REDIS_PORT),
  46. 'databases': 4,
  47. })
  48. cl.stdin.flush()
  49. cl.stdin.close()
  50. return cl
  51. def spawn_sentry():
  52. report('sentry', 'Launching sentry server')
  53. cl = Popen(['sentry', '--config=' + SENTRY_CONFIG, 'runserver',
  54. '-v', '0', '--noreload', '--nothreading',
  55. '--no-watchers', '--traceback',
  56. '127.0.0.1:%s' % settings.SENTRY_APIDOCS_WEB_PORT])
  57. return cl
  58. @contextmanager
  59. def management_connection():
  60. from sqlite3 import connect
  61. cfg = settings.DATABASES['default']
  62. con = connect(cfg['NAME'])
  63. try:
  64. con.cursor()
  65. yield con
  66. finally:
  67. con.close()
  68. def init_db():
  69. drop_db()
  70. report('db', 'Migrating database (this can time some time)')
  71. call_command('syncdb', migrate=True, interactive=False,
  72. traceback=True, verbosity=0)
  73. def drop_db():
  74. report('db', 'Dropping database')
  75. try:
  76. os.remove(settings.DATABASES['default']['NAME'])
  77. except (OSError, IOError):
  78. pass
  79. class SentryBox(object):
  80. def __init__(self):
  81. self.redis = None
  82. self.sentry = None
  83. self.task_runner = None
  84. def __enter__(self):
  85. self.redis = launch_redis()
  86. self.sentry = spawn_sentry()
  87. init_db()
  88. return self
  89. def __exit__(self, exc_type, exc_value, tb):
  90. drop_db()
  91. if self.redis is not None:
  92. report('redis', 'Stopping redis server')
  93. self.redis.kill()
  94. self.redis.wait()
  95. if self.sentry is not None:
  96. report('sentry', 'Shutting down sentry server')
  97. self.sentry.kill()
  98. self.sentry.wait()
  99. def dump_json(path, data):
  100. path = os.path.join(OUTPUT_PATH, path)
  101. try:
  102. os.makedirs(os.path.dirname(path))
  103. except OSError:
  104. pass
  105. with open(path, 'w') as f:
  106. for line in json.dumps(data, indent=2, sort_keys=True).splitlines():
  107. f.write(line.rstrip() + '\n')
  108. def run_scenario(vars, scenario_ident, func):
  109. runner = Runner(scenario_ident, func, **vars)
  110. report('scenario', 'Running scenario "%s"' % scenario_ident)
  111. func(runner)
  112. dump_json('scenarios/%s.json' % scenario_ident, runner.to_json())
  113. @click.command()
  114. @click.option('--output-path', type=click.Path())
  115. def cli(output_path):
  116. """API docs dummy generator."""
  117. global OUTPUT_PATH
  118. if output_path is not None:
  119. OUTPUT_PATH = os.path.abspath(output_path)
  120. with SentryBox():
  121. utils = MockUtils()
  122. report('org', 'Creating user and organization')
  123. user = utils.create_user('john@interstellar.invalid')
  124. org = utils.create_org('The Interstellar Jurisdiction',
  125. owner=user)
  126. api_key = utils.create_api_key(org)
  127. report('org', 'Creating team')
  128. team = utils.create_team('Powerful Abolitionist',
  129. org=org)
  130. projects = []
  131. for project_name in 'Pump Station', 'Prime Mover':
  132. report('project', 'Creating project "%s"' % project_name)
  133. project = utils.create_project(project_name, team=team, org=org)
  134. release = utils.create_release(project=project, user=user)
  135. report('event', 'Creating event for "%s"' % project_name)
  136. event1 = utils.create_event(project=project, release=release,
  137. platform='python')
  138. event2 = utils.create_event(project=project, release=release,
  139. platform='java')
  140. projects.append({
  141. 'project': project,
  142. 'release': release,
  143. 'events': [event1, event2],
  144. })
  145. vars = {
  146. 'org': org,
  147. 'api_key': api_key,
  148. 'me': user,
  149. 'api_key': api_key,
  150. 'teams': [{
  151. 'team': team,
  152. 'projects': projects,
  153. }],
  154. }
  155. for scenario_ident, func in iter_scenarios():
  156. run_scenario(vars, scenario_ident, func)
  157. section_mapping = {}
  158. report('docs', 'Exporting endpoint documentation')
  159. for endpoint in iter_endpoints():
  160. report('endpoint', 'Exporting docs for "%s"' %
  161. endpoint['endpoint_name'])
  162. section_mapping.setdefault(endpoint['section'], []) \
  163. .append((endpoint['endpoint_name'],
  164. endpoint['title']))
  165. dump_json('endpoints/%s.json' % endpoint['endpoint_name'], endpoint)
  166. report('docs', 'Exporting sections')
  167. dump_json('sections.json', {
  168. 'sections': dict((section, {
  169. 'title': title,
  170. 'entries': dict(section_mapping.get(section, ())),
  171. }) for section, title in get_sections().iteritems())
  172. })
  173. if __name__ == '__main__':
  174. cli()