text: # -*- coding: utf-8 -*-
  import datetime
  import json
  from .. import base
  
  from girder.api import access, describe, docs
  from girder.api.rest import Resource, filtermodel
  from girder.constants import AccessType, registerAccessFlag, VERSION
  from girder.models.setting import Setting
  from girder.models.user import User
  from girder.settings import SettingKey
  
  server = None
  Routes = [
      ('GET', (), ''),
      ('GET', (':id',), '/{id}'),
      ('UNKNOWN', (':id',), '/{id}'),
      ('GET', (':id', 'action'), '/{id}/action'),
      ('GET', ('action',), '/action'),
      ('PUT', ('action',), '/action'),
      ('POST', ('action',), '/action'),
      ('PATCH', ('action',), '/action'),
      ('DELETE', ('action',), '/action'),
      ('NEWMETHOD', ('action',), '/action'),
      ('UNKNOWN', ('action',), '/action'),
      ('GET', ('action', ':id'), '/action/{id}'),
      ('GET', ('noaction',), '/noaction')
      ]
  
  
  class DummyResource(Resource):
      def __init__(self):
          super().__init__()
          self.resourceName = 'foo'
          for method, pathElements, _testPath in Routes:
              self.route(method, pathElements, self.handler)
  
      @access.public
      @describe.describeRoute(
          describe.Description('Does nothing')
      )
      def handler(self, **kwargs):
          return kwargs
  
  
  testModel = {
      'id': 'Body',
      'require': 'bob',
      'properties': {
          'bob': {
              'type': 'array',
              'items': {
                  'type': 'integer'
              }
          }
      }
  }
  docs.addModel('Body', testModel, resources='model')
  
  globalModel = {
      'id': 'Global',
      'properties': {}
  }
  docs.addModel('Global', globalModel)
  
  
  class ModelResource(Resource):
      def __init__(self):
          super().__init__()
          self.resourceName = 'model'
          self.route('POST', (), self.hasModel)
  
      @access.public
      @describe.describeRoute(
          describe.Description('What a model')
          .param('body', 'Where its at!', dataType='Body', required=True,
                 paramType='body')
      )
      def hasModel(self, params):
          pass
  
  
  def setUpModule():
      global server
      server = base.startServer()
      server.root.api.v1.accesstest = DummyResource()
      server.root.api.v1.modeltest = ModelResource()
  
  
  def tearDownModule():
      base.stopServer()
  
  
  class ApiDescribeTestCase(base.TestCase):
      """
      Makes sure our swagger auto API docs are working.
      """
  
      def testApiBrandName(self):
          resp = self.request(path='', method='GET', isJson=False)
          self.assertStatusOk(resp)
          body = self.getBody(resp)
  
          defaultBrandName = Setting().getDefault(SettingKey.BRAND_NAME)
          self.assertTrue(('<title>%s - REST API Documentation</title>' % defaultBrandName) in body)
  
          Setting().set(SettingKey.BRAND_NAME, 'FooBar')
          # An other request to check if the brand name is set
          resp = self.request(path='', method='GET', isJson=False)
          self.assertStatusOk(resp)
          body = self.getBody(resp)
          self.assertTrue(('<title>%s - REST API Documentation</title>' % 'FooBar') in body)
  
          Setting().unset(SettingKey.BRAND_NAME)
          resp = self.request(path='', method='GET', isJson=False)
          self.assertStatusOk(resp)
          body = self.getBody(resp)
          self.assertTrue(('<title>%s - REST API Documentation</title>' % defaultBrandName) in body)
  
      def testInvalidResource(self):
          methods = ['DELETE', 'GET', 'PATCH', 'POST', 'PUT']
  
          for m in methods:
              resp = self.request(path='/not_valid', method=m, isJson=False)
              self.assertStatus(resp, 404)
  
          methods.remove('GET')
          for m in methods:
              resp = self.request(path='', method=m, isJson=False)
              self.assertStatus(resp, 405)
  
      def testApiDescribe(self):
          # Get coverage for serving the static swagger page
          resp = self.request(path='', method='GET', isJson=False)
          self.assertStatusOk(resp)
  
          # Test top level describe endpoint
          resp = self.request(path='/describe', method='GET')
          self.assertStatusOk(resp)
          self.assertHasKeys(resp.json, ('basePath', 'host', 'definitions',
                                         'info', 'paths', 'swagger', 'tags'))
          self.assertHasKeys(resp.json['info'], ('title', 'version'))
          self.assertEqual(resp.json['swagger'], describe.SWAGGER_VERSION)
          self.assertEqual(resp.json['info']['version'], VERSION['release'])
          self.assertIn('/group', resp.json['paths'])
          self.assertIn({'name': 'group'}, resp.json['tags'])
          self.assertHasKeys(
              resp.json['paths']['/group'], ('get', 'post'))
          self.assertHasKeys(
              resp.json['paths']['/group']['get'],
              ('operationId', 'parameters', 'responses'))
          self.assertHasKeys(
              resp.json['paths']['/group']['post'],
              ('operationId', 'parameters', 'responses'))
          self.assertGreater(len(
              resp.json['paths']['/group']['get']['operationId']), 0)
          self.assertGreater(len(
              resp.json['paths']['/group']['get']['parameters']), 0)
          self.assertGreater(len(
              resp.json['paths']['/group']['get']['responses']), 0)
          param = resp.json['paths']['/group']['get']['parameters'][0]
          self.assertHasKeys(
              param,
              ('description', 'in', 'name', 'required', 'type'))
          self.assertGreater(len(param['description']), 0)
          self.assertGreater(len(param['in']), 0)
          self.assertGreater(len(param['name']), 0)
          self.assertGreater(len(param['type']), 0)
          self.assertTrue(isinstance(param['required'], bool))
          self.assertIn(
              '200',
              resp.json['paths']['/group']['get']['responses'])
          self.assertIn(
              'description',
              resp.json['paths']['/group']['get']['responses']['200'])
  
      def testApiDescribeReferred(self):
          resp = self.request(path='/describe', method='GET')
          self.assertStatusOk(resp)
          self.assertEqual(resp.json['basePath'], '/api/v1')
          self.assertEqual(resp.json['host'], '127.0.0.1')
          resp = self.request(
              path='/describe', method='GET',
              additionalHeaders=[('Referer', 'http://somewhere.com/alternate/api/v1')])
          self.assertStatusOk(resp)
          self.assertEqual(resp.json['basePath'], '/alternate/api/v1')
          self.assertEqual(resp.json['host'], 'somewhere.com')
  
      def testRoutesExist(self):
          # Check that the resources and operations exist
          resp = self.request(path='/describe', method='GET')
          self.assertStatusOk(resp)
          self.assertHasKeys(
              resp.json['paths'],
              ('/foo', '/foo/{id}', '/foo/{id}/action', '/foo/action',
               '/foo/action/{id}', '/foo/noaction'))
          self.assertHasKeys(
              resp.json['paths']['/foo'],
              ('get',))
          self.assertEqual(len(resp.json['paths']['/foo']), 1)
          self.assertHasKeys(
              resp.json['paths']['/foo/{id}'],
              ('get', 'unknown'))
          self.assertEqual(len(resp.json['paths']['/foo/{id}']), 2)
          self.assertHasKeys(
              resp.json['paths']['/foo/{id}/action'],
              ('get',))
          self.assertEqual(len(resp.json['paths']['/foo/{id}/action']), 1)
          self.assertHasKeys(
              resp.json['paths']['/foo/action'],
              ('get', 'put', 'post', 'patch', 'delete', 'newmethod', 'unknown'))
          self.assertEqual(len(resp.json['paths']['/foo/action']), 7)
          self.assertHasKeys(
              resp.json['paths']['/foo/action/{id}'],
              ('get',))
          self.assertEqual(len(resp.json['paths']['/foo/action/{id}']), 1)
          self.assertHasKeys(
              resp.json['paths']['/foo/noaction'],
              ('get',))
          self.assertEqual(len(resp.json['paths']['/foo/noaction']), 1)
  
      def testAddModel(self):
          resp = self.request(path='/describe')
          self.assertStatusOk(resp)
          self.assertTrue('definitions' in resp.json)
          self.assertHasKeys(('Body', 'Global'), resp.json['definitions'])
          self.assertEqual(resp.json['definitions']['Body'], testModel)
          self.assertEqual(resp.json['definitions']['Global'], globalModel)
  
      def testAutoDescribeRoute(self):
          testRuns = []
  
          registerAccessFlag('my_flag', name='My flag')
  
          class AutoDescribe(Resource):
              def __init__(self):
                  super().__init__()
                  self.resourceName = 'auto_describe'
                  self.route('GET', ('test',), self.test)
                  self.route('POST', ('body',), self.body)
                  self.route('POST', ('json_body',), self.jsonBody)
                  self.route('POST', ('json_body_required',), self.jsonBodyRequired)
                  self.route('GET', ('model_param_flags',), self.hasModelParamFlags)
                  self.route('GET', ('model_param_query',), self.hasModelQueryParam)
                  self.route('GET', ('json_schema',), self.hasJsonSchema)
                  self.route('GET', ('missing_arg',), self.hasMissingArg)
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('test')
                  .param('b1', '', dataType='boolean', required=False, default=True)
                  .param('b2', '', dataType='boolean', required=False)
                  .param('float', '', dataType='number', required=False, default=1.0)
                  .param('integer', '', dataType='integer', required=False)
                  .param('timestamp', '', dataType='dateTime', required=False)
                  .param('datestamp', '', dataType='date', required=False)
                  .param('string', '', enum=['hello', 'world'], strip=True, lower=True)
                  .param('upper', '', required=False, upper=True)
                  .jsonParam('json1', '', required=False, requireArray=True)
                  .jsonParam('json2', '', required=False, requireObject=True, default={})
              )
              def test(self, b1, b2, string, upper, integer, float, timestamp, datestamp, json1,
                       json2, params):
                  testRuns.append({
                      'b1': b1,
                      'b2': b2,
                      'string': string,
                      'upper': upper,
                      'integer': integer,
                      'float': float,
                      'timestamp': timestamp,
                      'datestamp': datestamp,
                      'json1': json1,
                      'json2': json2
                  })
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('body')
                  .param('body', '', required=False, paramType='body')
              )
              def body(self, body):
                  testRuns.append({
                      'body': body
                  })
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('json_body')
                  .jsonParam('json_body', '', required=False, paramType='body')
              )
              def jsonBody(self, json_body):
                  testRuns.append({
                      'json_body': json_body
                  })
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('json_body_required')
                  .jsonParam('json_body', '', required=True, requireObject=True, paramType='body')
              )
              def jsonBodyRequired(self, json_body):
                  testRuns.append({
                      'json_body': json_body
                  })
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('has_model_param_query')
                  .modelParam('userId', model='user', level=AccessType.READ, paramType='query')
              )
              @filtermodel(model='user')
              def hasModelQueryParam(self, user):
                  return user
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('has_model_param_flags')
                  .modelParam('userId', model='user', level=AccessType.READ, paramType='query',
                              requiredFlags='my_flag')
              )
              def hasModelParamFlags(self, user):
                  return user
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('has_json_schema')
                  .jsonParam('obj', '', schema={
                      'type': 'object',
                      'required': ['foo', 'bar']
                  })
              )
              def hasJsonSchema(self, obj):
                  return obj
  
              @access.public
              @describe.autoDescribeRoute(
                  describe.Description('has_missing_arg')
                  .param('foo', '')
              )
              def hasMissingArg(self, params):
                  return params
  
          server.root.api.v1.auto_describe = AutoDescribe()
  
          def testBad(inputs, expected):
              resp = self.request('/auto_describe/test', params=inputs)
              self.assertStatus(resp, 400)
              self.assertEqual(testRuns, [])
              self.assertEqual(resp.json['message'], expected)
  
          def testOk(inputs, expected):
              resp = self.request('/auto_describe/test', params=inputs)
              self.assertStatusOk(resp)
              self.assertEqual(len(testRuns), 1)
              self.assertEqual(testRuns[0], expected)
              del testRuns[-1]
  
          testBad({}, 'Parameter "string" is required.')
          testBad({
              'string': 'invalid value'
          }, 'Invalid value for string: "invalid value". Allowed values: hello, world.')
          testBad({
              'string': 'hello',
              'float': 'not a float'
          }, 'Invalid value for numeric parameter float: not a float.')
  
          testBad({
              'string': 'hello',
              'integer': '7.5'
          }, 'Invalid value for integer parameter integer: 7.5.')
  
          testBad({
              'string': 'hello',
              'timestamp': 'hello world'
          }, 'Invalid date format for parameter timestamp: hello world.')
  
          testBad({
              'string': 'hello',
              'datestamp': 'not a date'
          }, 'Invalid date format for parameter datestamp: not a date.')
  
          testBad({
              'string': 'hello',
              'json1': json.dumps({'hello': 'world'})
          }, 'Parameter json1 must be a JSON array.')
  
          testBad({
              'string': 'hello',
              'json2': json.dumps(['hello', 'world'])
          }, 'Parameter json2 must be a JSON object.')
  
          testOk({
              'string': '  WoRlD '
          }, {
              'string': 'world',
              'upper': None,
              'b1': True,
              'b2': None,
              'integer': None,
              'float': 1.,
              'json1': None,
              'json2': {},
              'timestamp': None,
              'datestamp': None
          })
  
          testOk({
              'string': ' hello',
              'upper': ' hello',
              'b1': 'false',
              'b2': 'true',
              'integer': '3',
              'float': '0.25',
              'json1': json.dumps([1, 2, 'abc']),
              'json2': json.dumps({'hello': 'world'}),
              'timestamp': '2017-01-01T11:35:22',
              'datestamp': '2017-02-02T11:33:22'
          }, {
              'string': 'hello',
              'upper': ' HELLO',
              'b1': False,
              'b2': True,
              'integer': 3,
              'float': 0.25,
              'json1': [1, 2, 'abc'],
              'json2': {'hello': 'world'},
              'timestamp': datetime.datetime(2017, 1, 1, 11, 35, 22),
              'datestamp': datetime.date(2017, 2, 2)
          })
  
          # Test request body
          body = 'torso'
          resp = self.request('/auto_describe/body', method='POST',
                              body=json.dumps(body), type='application/json')
          self.assertStatusOk(resp)
          self.assertEqual(len(testRuns), 1)
          self.assertTrue('body' in testRuns[0])
          self.assertTrue(hasattr(testRuns[0]['body'], 'read'))
          del testRuns[-1]
  
          # Test request JSON body (optional)
          body = {
              'emmet': 'otter'
          }
          resp = self.request('/auto_describe/json_body', method='POST',
                              body=json.dumps(body), type='application/json')
          self.assertStatusOk(resp)
          self.assertEqual(len(testRuns), 1)
          expected = {
              'json_body': body
          }
          self.assertEqual(testRuns[0], expected)
          del testRuns[-1]
  
          # Test request JSON body (optional), omitting body
          resp = self.request('/auto_describe/json_body', method='POST')
          self.assertStatusOk(resp)
          self.assertEqual(len(testRuns), 1)
          expected = {
              'json_body': None
          }
          self.assertEqual(testRuns[0], expected)
          del testRuns[-1]
  
          # Test request JSON body (required)
          body = {
              'emmet': 'otter'
          }
          resp = self.request('/auto_describe/json_body_required', method='POST',
                              body=json.dumps(body), type='application/json')
          self.assertStatusOk(resp)
          self.assertEqual(len(testRuns), 1)
          expected = {
              'json_body': body
          }
          self.assertEqual(testRuns[0], expected)
          del testRuns[-1]
  
          # Test request JSON body (required), omitting body
          resp = self.request('/auto_describe/json_body_required', method='POST')
          self.assertStatus(resp, 400)
  
          # Test request JSON body (required), pass list
          body = [{
              'emmet': 'otter'
          }]
          resp = self.request('/auto_describe/json_body_required', method='POST',
                              body=json.dumps(body), type='application/json')
          self.assertStatus(resp, 400)
  
          # Test omission of required modelParam
          resp = self.request('/auto_describe/model_param_query')
          self.assertStatus(resp, 400)
          self.assertEqual(resp.json['message'], 'Parameter "userId" is required.')
  
          resp = self.request('/auto_describe/model_param_query', params={'userId': None})
          self.assertStatus(resp, 400)
          self.assertEqual(resp.json['message'], 'Invalid ObjectId: None')
  
          user = User().createUser(
              firstName='admin', lastName='admin', email='a@admin.com', login='admin',
              password='password')
          resp = self.request(
              '/auto_describe/model_param_query', user=user, params={'userId': user['_id']})
          self.assertStatusOk(resp)
  
          # Test requiredFlags in modelParam
          resp = self.request('/auto_describe/model_param_flags', params={
              'userId': user['_id']
          })
          self.assertStatus(resp, 401)
          self.assertRegex(resp.json['message'], '^Access denied for user')
  
          resp = self.request('/auto_describe/json_schema', params={
              'obj': json.dumps([])
          })
          self.assertStatus(resp, 400)
          self.assertRegex(
              resp.json['message'],
              r"^Invalid JSON object for parameter obj: \[\] is not of type 'object'")
  
          resp = self.request('/auto_describe/json_schema', params={
              'obj': json.dumps({})
          })
          self.assertStatus(resp, 400)
          self.assertRegex(
              resp.json['message'],
              r"^Invalid JSON object for parameter obj: 'foo' is a required property")
  
          obj = {
              'foo': 1,
              'bar': 2
          }
          resp = self.request('/auto_describe/json_schema', params={
              'obj': json.dumps(obj)
          })
          self.assertStatusOk(resp)
          self.assertEqual(resp.json, obj)
  
          # Test missing arg in wrapped function, should fall through to params dict
          resp = self.request('/auto_describe/missing_arg', params={
              'foo': 'bar'
          })
          self.assertStatusOk(resp)
          self.assertEqual(resp.json, {'foo': 'bar'})
  
      def testDeprecatedRoute(self):
          """
          Test that a route marked as deprecated is described as deprecated.
          """
          class OldResource(Resource):
              def __init__(self):
                  super().__init__()
                  self.resourceName = 'old_resource'
                  self.route('GET', (), self.handler)
                  self.route('GET', ('deprecated',), self.deprecatedHandler)
  
              @access.public
              @describe.describeRoute(
                  describe.Description('Handler')
              )
              def handler(self, params):
                  return None
  
              @access.public
              @describe.describeRoute(
                  describe.Description('Deprecated handler')
                  .deprecated()
              )
              def deprecatedHandler(self, params):
                  return None
  
          server.root.api.v1.old_resource = OldResource()
  
          resp = self.request(path='/describe', method='GET')
          self.assertStatusOk(resp)
          self.assertIn('paths', resp.json)
          self.assertIn('/old_resource', resp.json['paths'])
          self.assertIn('/old_resource/deprecated', resp.json['paths'])
          self.assertIn('get', resp.json['paths']['/old_resource'])
          self.assertNotIn('deprecated', resp.json['paths']['/old_resource']['get'])
          self.assertIn('get', resp.json['paths']['/old_resource/deprecated'])
          self.assertIn('deprecated', resp.json['paths']['/old_resource/deprecated']['get'])
          self.assertTrue(resp.json['paths']['/old_resource/deprecated']['get']['deprecated'])
  
      def testProduces(self):
          """
          Test that a route marked as producing a list of mime types reports
          that information properly.
          """
          class ProducesResource(Resource):
              def __init__(self):
                  super().__init__()
                  self.resourceName = 'produces_resource'
                  self.route('GET', (), self.handler)
                  self.route('GET', ('produces1',), self.producesHandler)
                  self.route('GET', ('produces2',), self.produces2Handler)
  
              @access.public
              @describe.describeRoute(
                  describe.Description('Handler')
              )
              def handler(self, params):
                  return None
  
              @access.public
              @describe.describeRoute(
                  describe.Description('Produces handler')
                  .produces('image/jpeg')
              )
              def producesHandler(self, params):
                  return None
  
              @access.public
              @describe.describeRoute(
                  describe.Description('Produces 2 handler')
                  .produces('image/tiff')
                  .produces(['image/jpeg', 'image/png'])
              )
              def produces2Handler(self, params):
                  return None
  
          server.root.api.v1.produces_resource = ProducesResource()
  
          resp = self.request(path='/describe', method='GET')
          self.assertStatusOk(resp)
          self.assertIn('paths', resp.json)
          self.assertIn('/produces_resource', resp.json['paths'])
          self.assertIn('/produces_resource/produces1', resp.json['paths'])
          self.assertIn('/produces_resource/produces2', resp.json['paths'])
          self.assertNotIn('produces', resp.json['paths']['/produces_resource']['get'])
          self.assertIn('produces', resp.json['paths']['/produces_resource/produces1']['get'])
          self.assertEqual(resp.json['paths']['/produces_resource/produces1']['get']['produces'],
                           ['image/jpeg'])
          self.assertEqual(resp.json['paths']['/produces_resource/produces2']['get']['produces'],
                           ['image/tiff', 'image/jpeg', 'image/png'])
  