def render_examples(self): examples_blocks = [] for example_name in sorted(self.examples.keys()): example_response = self.examples[example_name]['response'] examples_blocks.append( normalize_indentation( ''' "{example_name}": {example_response} ''', 0).format(example_name=example_name, example_response=json.dumps(example_response, sort_keys=True, indent=4))) return normalize_indentation( ''' /** * Examples for {self.name} */ export const {command_name}Examples = {{ {examples} }} ''', 0).format(self=self, command_name=to_camelcase(self.name), examples=normalize_indentation( ',\n\n'.join(examples_blocks), 4))
def render(self): if self.get_bulk_read_field() and self.method != 'get': return normalize_indentation( ''' {self.header} public {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.client .{self.method}<X.{self.response.name}>({self.signature.call_args}) .pipe(map(x => x.{bulk_read_field})); }} ''', 0).format( # noqa self=self, bulk_read_field=self.get_bulk_read_field(), ) elif self.get_bulk_read_field() or self.method == 'get': return normalize_indentation( ''' {self.header} public {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.client.get<X.{self.response.name}>({self.signature.call_args}); }} ''', 0).format(self=self) # noqa else: return normalize_indentation( ''' {self.header} public {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.client .{self.method}<X.{self.response.name}>({self.signature.call_args}) .pipe(filter(x => !_.isEmpty(x))); }} ''', 0).format(self=self) # noqa
def render_facade(self): if self.get_bulk_read_field(): return normalize_indentation( ''' {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.{self.domain_id}Domain.{self.camel_name}({self.signature.call_args_without_path}); }} ''', 0).format(self=self) # noqa elif self.method == 'get': return normalize_indentation( ''' {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.{self.domain_id}Domain.{self.camel_name}({self.signature.call_args_without_path}); }} ''', 0).format(self=self) # noqa else: return normalize_indentation( ''' {self.camel_name}({self.signature.input}): Observable<X.{self.response.name}> {{ return this.{self.domain_id}Domain.{self.camel_name}({self.signature.call_args_without_path}); }} ''', 0).format(self=self) # noqa
def render_domain_ts(self, domain, commands): blocks = [ normalize_indentation( ''' {autogenerated_doc} /** * {domain.name} Domain */ import {{ Injectable }} from '@angular/core'; import {{ filter }} from 'rxjs/operators'; import {{ Observable }} from 'rxjs'; import * as _ from 'underscore'; import {{ HttpService }} from '../../http.service'; import * as X from './{domain.id}.models'; @Injectable() export class {domain.camel_id}Domain {{ constructor(private client: HttpService) {{}} ''', 0).format( # noqa autogenerated_doc=self.AUTOGENERATED_DOC, domain=domain, ) ] for command in commands: blocks.append(normalize_indentation(command.render(), 4)) blocks.append('}') path = domain.path.join('{}.domain.ts'.format(domain.id)) with open(path, 'w') as f: f.write('\n\n'.join(blocks))
def render_models_ts(self, domain, commands): model_enums = [] models = [] for command in commands: # -- REQUEST_QUERY b, enums = command.request_query.render() if b: models.append(b) if enums: model_enums.extend(enums) # -- REQUEST_BODY b, enums = command.request_body.render() if b: models.append(b) if enums: model_enums.extend(enums) # -- RESPONSE b, enums = command.response.render() if b: models.append(b) if enums: model_enums.extend(enums) enums_block = '' if model_enums: sorted_enum_names = sorted(set([e.name for e in model_enums])) enums_block = normalize_indentation( ''' import {{ {enums} }} from '../../shared/enums'; ''', 0).format(enums=', '.join(sorted_enum_names)) blocks = [ normalize_indentation( ''' {autogenerated_doc} {enums_block} /** * {domain_name} Domain Models */ ''', 0).format(enums_block=enums_block, autogenerated_doc=self.AUTOGENERATED_DOC, domain_name=domain.name), *models, ] path = domain.path.join('{}.models.ts'.format(domain.id)) with open(path, 'w') as f: f.write('\n\n'.join(blocks))
def render_api_ts(self, commands_by_domain): blocks = [ normalize_indentation( ''' {autogenerated_doc} /** * Facade API Service for all domains */ import {{ Injectable, Injector }} from '@angular/core'; import {{ Observable }} from 'rxjs'; import * as X from '../domains/index'; @Injectable() export class APIService {{ constructor(private injector: Injector) {{}} ''', 0).format(autogenerated_doc=self.AUTOGENERATED_DOC, ) ] for domain in sorted(commands_by_domain.keys(), key=lambda x: x.id): commands = commands_by_domain[domain] blocks.append( normalize_indentation( ''' /** * {domain.name} domain */ private _{domain.id}Domain: X.{domain.camel_id}Domain; public get {domain.id}Domain(): X.{domain.camel_id}Domain {{ if (!this._{domain.id}Domain) {{ this._{domain.id}Domain = this.injector.get(X.{domain.camel_id}Domain); }} return this._{domain.id}Domain; }} ''', 4).format( # noqa domain=domain, )) for command_name in sorted(commands.keys()): command = commands[command_name] blocks.append(normalize_indentation(command.render_facade(), 4)) blocks.append('}') path = os.path.join(self.repo.base_path, 'projects/client/src/services/api.service.ts') with open(path, 'w') as f: f.write('\n\n'.join(blocks))
def test_render_access_ts(self): shared_path = str(self.src_dir.mkdir('shared')) commands = [ Mock(render_access=Mock( return_value=( 'BULK_READ_WHAT: [AccountType.LEARNER, AccountType.MENTOR]' ))), Mock(render_access=Mock(return_value='BULK_DELETE_THIS: null')), Mock( render_access=Mock( return_value='CREATE: [AccountType.ADMIN]')), ] assert os.listdir(shared_path) == [] self.renderer.render_access_ts(shared_path, commands) assert os.listdir(shared_path) == ['access.ts'] with open(os.path.join(shared_path, 'access.ts'), 'r') as f: result = f.read() assert result == normalize_indentation(''' /** * THIS FILE WAS AUTOGENERATED, ALL MANUAL CHANGES CAN BE * OVERWRITTEN */ import { AccountType } from './enums'; export const Access = { BULK_READ_WHAT: [AccountType.LEARNER, AccountType.MENTOR], BULK_DELETE_THIS: null, CREATE: [AccountType.ADMIN], } ''', 0) # noqa
def test_render__get_bulk_read_field__get(self): self.mocker.patch.object(Command, 'get_bulk_read_field').return_value = 'people' command = Command( 'READ_TASK', { 'method': 'GET', 'meta': { 'title': 'Read Task', 'domain': { 'id': 'tasks', 'name': 'Tasks Management', } }, 'access': { 'is_private': True, 'access_list': ['LEARNER'], }, 'path_conf': { 'path': '/tasks/{task_id}', 'parameters': [ { 'name': 'task_id', 'type': 'integer', }, ] }, 'schemas': { 'input_query': { 'schema': { 'hi': 'there' }, 'uri': '' }, 'input_body': None, 'output': { 'schema': { 'type': 'object', 'required': ['people'], 'properties': { 'people': { 'type': 'array', }, }, }, 'uri': '', }, }, 'examples': {}, }) assert command.render() == normalize_indentation( ''' /** * Read Task */ public readTask(taskId: any, params: X.ReadTaskQuery): Observable<X.ReadTaskResponse> { return this.client.get<X.ReadTaskResponse>(`/tasks/${taskId}`, { params, authorizationRequired: true }); } ''', 0) # noqa
def test_command__default_values(self): render = self.mocker.patch.object(AngularClientRenderer, 'render') render.return_value = '1.0.19' result = self.runner.invoke(command, ['origin', 'super']) assert result.exit_code == 0 assert normalize_indentation(result.output, 0) == normalize_indentation( ''' - Successfully rendered and pushed Angular Client version: 1.0.19 ''', 0) # noqa assert render.call_args_list == [ call(exclude_domains=(), include_domains=(), only_build=None), ]
def test_render_examples_ts(self): paths_path = str(self.domains_dir.mkdir('paths')) domain = Domain('paths', 'Path Management') commands = [ Mock(render_examples=Mock(return_value='command 1')), Mock(render_examples=Mock(return_value='command 2')), ] assert os.listdir(paths_path) == [] self.renderer.render_examples_ts(domain, commands) assert os.listdir(paths_path) == ['paths.examples.ts'] with open(os.path.join(paths_path, 'paths.examples.ts'), 'r') as f: assert f.read() == normalize_indentation(''' /** * THIS FILE WAS AUTOGENERATED, ALL MANUAL CHANGES CAN BE * OVERWRITTEN */ /** * Path Management Domain Examples */ command 1 command 2 ''', 0) # noqa
def header(self): return normalize_indentation( ''' /** * {self.title} */ ''', 0).format(self=self)
def render_index_ts(self, domain): with open(domain.path.join('index.ts'), 'w') as f: f.write( normalize_indentation( ''' export * from './{domain.id}.domain'; export * from './{domain.id}.models'; ''', 0).format(domain=domain))
def test_render__numerical(self): enum0 = Enum('position', [0, 1]) enum1 = Enum('position', ['0', '1', '2']) assert enum0.render() == normalize_indentation(''' export enum Position { VALUE_0 = 0, VALUE_1 = 1, } ''', 0) assert enum1.render() == normalize_indentation(''' export enum Position { VALUE_0 = '0', VALUE_1 = '1', VALUE_2 = '2', } ''', 0)
def test_render(self): enum = Enum('age', ['AA', 'BB']) assert enum.render() == normalize_indentation(''' export enum Age { AA = 'AA', BB = 'BB', } ''', 0)
def test_command__makes_the_right_calls(self): renderer = self.mocker.patch( # noqa 'lily.docs.management.commands.render_angular' '.AngularClientRenderer') result = self.runner.invoke(command, ['origin', 'super', '--only_build']) assert result.exit_code == 0 assert normalize_indentation(result.output, 0) == normalize_indentation( ''' - Successfully rendered and built Angular Client [NO PUSHING TO REMOTE] ''', 0) # noqa assert renderer.call_args_list == [call('origin', 'super')] assert renderer.return_value.render.call_args_list == [ call(exclude_domains=(), include_domains=(), only_build=True), ]
def test_render__removes_duplicates(self): enum = Enum('class', ['AA', 'BB', 'AA']) assert enum.render() == normalize_indentation(''' export enum Class { AA = 'AA', BB = 'BB', } ''', 0)
def test_render__starts_from_number(self): enum = Enum('quality', ['240p', '720p', '1080p']) assert enum.render() == normalize_indentation(''' export enum Quality { VALUE_1080P = '1080p', VALUE_240P = '240p', VALUE_720P = '720p', } ''', 0)
def test_render__sorts_values(self): enum = Enum('category', ['XX', 'AA', 'BB']) assert enum.render() == normalize_indentation(''' export enum Category { AA = 'AA', BB = 'BB', XX = 'XX', } ''', 0)
def render_examples_ts(self, domain, commands): blocks = [ normalize_indentation( ''' {autogenerated_doc} /** * {domain.name} Domain Examples */ ''', 0).format( # noqa autogenerated_doc=self.AUTOGENERATED_DOC, domain=domain, ) ] for command in commands: blocks.append(normalize_indentation(command.render_examples(), 0)) path = domain.path.join('{}.examples.ts'.format(domain.id)) with open(path, 'w') as f: f.write('\n\n'.join(blocks))
def test_render__contains_extra_characters(self): enum = Enum( 'content_type', ['image/svg+xml', 'video/png', 'audio/ogg-what']) assert enum.render() == normalize_indentation(''' export enum ContentType { AUDIO_OGG_WHAT = 'audio/ogg-what', IMAGE_SVG_XML = 'image/svg+xml', VIDEO_PNG = 'video/png', } ''', 0)
def command(client_origin, client_prefix, only_build, include_domain, exclude_domain): """Render Angular Client based on the declared commands.""" rendered_version = AngularClientRenderer( client_origin, client_prefix).render(only_build=only_build, include_domains=include_domain, exclude_domains=exclude_domain) if only_build: click.secho(normalize_indentation( ''' - Successfully rendered and built Angular Client [NO PUSHING TO REMOTE] ''', 0), fg='green') # noqa else: click.secho(normalize_indentation( ''' - Successfully rendered and pushed Angular Client version: {version} '''.format(version=rendered_version), 0), fg='green') # noqa
def test_render_index_ts(self): paths_path = str(self.domains_dir.mkdir('paths')) assert os.listdir(paths_path) == [] self.renderer.render_index_ts(Domain('paths', 'Path Management')) assert os.listdir(paths_path) == ['index.ts'] with open(os.path.join(paths_path, 'index.ts'), 'r') as f: assert f.read() == normalize_indentation(''' export * from './paths.domain'; export * from './paths.models'; ''', 0)
def test_render_models_ts__without_enums(self): paths_path = str(self.domains_dir.mkdir('paths')) domain = Domain('paths', 'Path Management') commands = [ Mock( request_query=Mock( render=Mock(return_value=['request 1', []])), request_body=Mock( render=Mock(return_value=['', []])), response=Mock( render=Mock(return_value=['response 1', []])), ), Mock( request_query=Mock( render=Mock(return_value=['request 2', []])), request_body=Mock( render=Mock(return_value=['', []])), response=Mock( render=Mock(return_value=['response 2', []])), ), ] assert os.listdir(paths_path) == [] self.renderer.render_models_ts(domain, commands) assert os.listdir(paths_path) == ['paths.models.ts'] with open(os.path.join(paths_path, 'paths.models.ts'), 'r') as f: assert f.read() == normalize_indentation(''' /** * THIS FILE WAS AUTOGENERATED, ALL MANUAL CHANGES CAN BE * OVERWRITTEN */ /** * Path Management Domain Models */ request 1 response 1 request 2 response 2 ''', 0)
def test_render__post(self): command = Command( 'READ_TASK', { 'method': 'POST', 'meta': { 'title': 'Create Path', 'domain': { 'id': 'paths', 'name': 'Paths Management', } }, 'access': { 'is_private': True, 'access_list': ['LEARNER'], }, 'path_conf': { 'path': '/paths/', 'parameters': [] }, 'schemas': { 'input_query': None, 'input_body': { 'schema': { 'hi': 'there' }, 'uri': '' }, 'output': { 'schema': { 'hi': 'there' }, 'uri': '' }, }, 'examples': {}, }) assert command.render() == normalize_indentation( ''' /** * Create Path */ public readTask(body: X.ReadTaskBody): Observable<X.ReadTaskResponse> { return this.client .post<X.ReadTaskResponse>('/paths/', body, { authorizationRequired: true }) .pipe(filter(x => !_.isEmpty(x))); } ''', 0) # noqa
def test_render_facade__get(self): command = Command( 'READ_TASK', { 'method': 'GET', 'meta': { 'title': 'Read Task', 'domain': { 'id': 'tasks', 'name': 'Tasks Management', } }, 'access': { 'is_private': True, 'access_list': ['LEARNER'], }, 'path_conf': { 'path': '/tasks/{task_id}', 'parameters': [ { 'name': 'task_id', 'type': 'integer', }, ] }, 'schemas': { 'input_query': { 'schema': { 'hi': 'there' }, 'uri': '' }, 'input_body': None, 'output': { 'schema': { 'hi': 'there' }, 'uri': '' }, }, 'examples': {}, }) assert command.render_facade() == normalize_indentation( ''' readTask(taskId: any, params: X.ReadTaskQuery): Observable<X.ReadTaskResponse> { return this.tasksDomain.readTask(taskId, params); } ''', 0) # noqa
def test_render_api_index_ts(self): commands_by_domain = { Domain('cards', 'Cards Management'): {}, Domain('recall', 'Recall Management'): {}, Domain('paths', 'Path Management'): {}, } self.renderer.render_api_index_ts(commands_by_domain) assert os.listdir(self.domains_dir) == ['index.ts'] assert ( self.domains_dir.join('index.ts').read() == normalize_indentation(''' export * from './cards/index'; export * from './paths/index'; export * from './recall/index'; ''', 0)) # noqa
def test_render_facade__post(self): command = Command( 'READ_TASK', { 'method': 'POST', 'meta': { 'title': 'Create Path', 'domain': { 'id': 'paths', 'name': 'Paths Management', } }, 'access': { 'is_private': True, 'access_list': ['LEARNER'], }, 'path_conf': { 'path': '/paths/', 'parameters': [] }, 'schemas': { 'input_query': None, 'input_body': { 'schema': { 'hi': 'there' }, 'uri': '' }, 'output': { 'schema': { 'hi': 'there' }, 'uri': '' }, }, 'examples': {}, }) assert command.render_facade() == normalize_indentation( ''' readTask(body: X.ReadTaskBody): Observable<X.ReadTaskResponse> { return this.pathsDomain.readTask(body); } ''', 0) # noqa
def test_render_domain_ts(self): paths_path = str(self.domains_dir.mkdir('paths')) domain = Domain('paths', 'Path Management') commands = [ Mock(render=Mock(return_value='command 1')), Mock(render=Mock(return_value='command 2')), ] assert os.listdir(paths_path) == [] self.renderer.render_domain_ts(domain, commands) assert os.listdir(paths_path) == ['paths.domain.ts'] with open(os.path.join(paths_path, 'paths.domain.ts'), 'r') as f: assert f.read() == normalize_indentation(''' /** * THIS FILE WAS AUTOGENERATED, ALL MANUAL CHANGES CAN BE * OVERWRITTEN */ /** * Path Management Domain */ import { Injectable } from '@angular/core'; import { filter } from 'rxjs/operators'; import { Observable } from 'rxjs'; import * as _ from 'underscore'; import { HttpService } from '../../http.service'; import * as X from './paths.models'; @Injectable() export class PathsDomain { constructor(private client: HttpService) {} command 1 command 2 } ''', 0) # noqa
def test_render_examples(self): conf = deepcopy(CONF) conf['examples'] = { '404 (NOT_FOUND)': { 'response': { 'where': 'here', 'error': 'not found', 'status': 404, }, }, '200 (YO)': { 'response': { 'id': 45, 'age': 879, }, }, } command = Command('BULK_READ_TASKS', conf) assert (remove_white_chars( command.render_examples()) == remove_white_chars( normalize_indentation( ''' /** * Examples for BULK_READ_TASKS */ export const BulkReadTasksExamples = { "200 (YO)": { "age": 879, "id": 45 }, "404 (NOT_FOUND)": { "error": "not found", "status": 404, "where": "here" } } ''', 0)))
def render_access_ts(self, rel_path, all_commands): header = normalize_indentation( ''' {autogenerated_doc} ''', 0).format( # noqa autogenerated_doc=self.AUTOGENERATED_DOC) access_enum = settings.LILY_AUTHORIZER_ACCESS_ENUM_CLASS blocks = [ command.render_access(access_enum) for command in all_commands ] blocks = '\n'.join([f' {b},' for b in blocks]) abs_path = os.path.join(self.repo.base_path, rel_path) with open(os.path.join(abs_path, 'access.ts'), 'w') as f: f.write( f'{header}\n\n' f'import {{ { access_enum } }} from \'./enums\';\n\n' # noqa f'export const Access = {{\n' f'{blocks}\n' '}')