diff --git a/.dockerignore b/.dockerignore index f275b9a3..88dcdd3e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ .vscode +.idea node_modules test .editorconfig @@ -8,4 +9,4 @@ test appveyor.yml icon.png LICENSE -README.md \ No newline at end of file +README.md diff --git a/.editorconfig b/.editorconfig index 2f35c357..722112c1 100755 --- a/.editorconfig +++ b/.editorconfig @@ -1,20 +1,655 @@ -# @w3tec -# http://editorconfig.org - -root = true - [*] -indent_style = space -indent_size = 4 -end_of_line = lf charset = utf-8 -trim_trailing_whitespace = true +end_of_line = lf +indent_size = 4 +indent_style = space insert_final_newline = true +max_line_length = 140 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_wrap_on_typing = false -[*.md] -trim_trailing_whitespace = false +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_brace_placement = 0 +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = 0 -# Use 2 spaces since npm does not respect custom indentation settings -[package.json] -indent_style = space +[*.feature] +indent_size = 2 +ij_gherkin_keep_indents_on_empty_lines = false + +[*.haml] +indent_size = 2 +ij_haml_keep_indents_on_empty_lines = false + +[*.less] +indent_size = 2 +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_brace_placement = 0 +ij_less_enforce_quotes_on_format = false +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_use_double_quotes = true +ij_less_value_alignment = 0 + +[*.sass] +indent_size = 2 +ij_sass_align_closing_brace_with_properties = false +ij_sass_blank_lines_around_nested_selector = 1 +ij_sass_blank_lines_between_blocks = 1 +ij_sass_brace_placement = 0 +ij_sass_enforce_quotes_on_format = false +ij_sass_hex_color_long_format = false +ij_sass_hex_color_lower_case = false +ij_sass_hex_color_short_format = false +ij_sass_hex_color_upper_case = false +ij_sass_keep_blank_lines_in_code = 2 +ij_sass_keep_indents_on_empty_lines = false +ij_sass_keep_single_line_blocks = false +ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_space_after_colon = true +ij_sass_space_before_opening_brace = true +ij_sass_use_double_quotes = true +ij_sass_value_alignment = 0 + +[*.scss] +indent_size = 2 +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_brace_placement = 0 +ij_scss_enforce_quotes_on_format = false +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 2 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_use_double_quotes = true +ij_scss_value_alignment = 0 + +[*.styl] +indent_size = 2 +ij_stylus_align_closing_brace_with_properties = false +ij_stylus_blank_lines_around_nested_selector = 1 +ij_stylus_blank_lines_between_blocks = 1 +ij_stylus_brace_placement = 0 +ij_stylus_enforce_quotes_on_format = false +ij_stylus_hex_color_long_format = false +ij_stylus_hex_color_lower_case = false +ij_stylus_hex_color_short_format = false +ij_stylus_hex_color_upper_case = false +ij_stylus_keep_blank_lines_in_code = 2 +ij_stylus_keep_indents_on_empty_lines = false +ij_stylus_keep_single_line_blocks = false +ij_stylus_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_stylus_space_after_colon = true +ij_stylus_space_before_opening_brace = true +ij_stylus_use_double_quotes = true +ij_stylus_value_alignment = 0 + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ats,*.ts}] +indent_size = 2 +xmax_line_length = 200 +tab_width = 2 +ij_continuation_indent_size = 2 +ij_smart_tabs = true +ij_visual_guides = 80 +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = false +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = normal +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = true +ij_typescript_call_parameters_wrap = on_every_item +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = always +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = whenmultiline +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = always +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = true +ij_typescript_force_semicolon_style = true +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = always +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = normal +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = true +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 1 +ij_typescript_keep_first_column_comment = false +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = normal +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = off +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = false +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = true +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = true +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = false +ij_typescript_use_explicit_js_extension = global +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = always +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + +[{*.cjs,*.js}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 2 +ij_visual_guides = 80 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = off +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = off +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = whenmultiline +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = never +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = off +ij_javascript_force_quote_style = true +ij_javascript_force_semicolon_style = true +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = false +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 2 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = off +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = false +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = true +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = true +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = off +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = false +ij_javascript_use_explicit_js_extension = global +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.cjsx,*.coffee}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 2 +ij_coffeescript_align_function_body = false +ij_coffeescript_align_imports = false +ij_coffeescript_align_multiline_array_initializer_expression = true +ij_coffeescript_align_multiline_parameters = true +ij_coffeescript_align_multiline_parameters_in_calls = false +ij_coffeescript_align_object_properties = 0 +ij_coffeescript_align_union_types = false +ij_coffeescript_align_var_statements = 0 +ij_coffeescript_array_initializer_new_line_after_left_brace = false +ij_coffeescript_array_initializer_right_brace_on_new_line = false +ij_coffeescript_array_initializer_wrap = normal +ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_coffeescript_blank_lines_around_function = 1 +ij_coffeescript_call_parameters_new_line_after_left_paren = false +ij_coffeescript_call_parameters_right_paren_on_new_line = false +ij_coffeescript_call_parameters_wrap = normal +ij_coffeescript_chained_call_dot_on_new_line = true +ij_coffeescript_comma_on_new_line = false +ij_coffeescript_enforce_trailing_comma = keep +ij_coffeescript_field_prefix = _ +ij_coffeescript_file_name_style = relaxed +ij_coffeescript_force_quote_style = false +ij_coffeescript_force_semicolon_style = false +ij_coffeescript_function_expression_brace_style = end_of_line +ij_coffeescript_import_merge_members = global +ij_coffeescript_import_prefer_absolute_path = global +ij_coffeescript_import_sort_members = true +ij_coffeescript_import_sort_module_name = false +ij_coffeescript_import_use_node_resolution = true +ij_coffeescript_imports_wrap = on_every_item +ij_coffeescript_indent_chained_calls = true +ij_coffeescript_indent_package_children = 0 +ij_coffeescript_jsx_attribute_value = braces +ij_coffeescript_keep_blank_lines_in_code = 2 +ij_coffeescript_keep_first_column_comment = true +ij_coffeescript_keep_indents_on_empty_lines = false +ij_coffeescript_keep_line_breaks = true +ij_coffeescript_keep_simple_methods_in_one_line = false +ij_coffeescript_method_parameters_new_line_after_left_paren = false +ij_coffeescript_method_parameters_right_paren_on_new_line = false +ij_coffeescript_method_parameters_wrap = off +ij_coffeescript_object_literal_wrap = on_every_item +ij_coffeescript_prefer_as_type_cast = false +ij_coffeescript_reformat_c_style_comments = false +ij_coffeescript_space_after_comma = true +ij_coffeescript_space_after_dots_in_rest_parameter = false +ij_coffeescript_space_after_generator_mult = true +ij_coffeescript_space_after_property_colon = true +ij_coffeescript_space_after_type_colon = true +ij_coffeescript_space_after_unary_not = false +ij_coffeescript_space_before_async_arrow_lparen = true +ij_coffeescript_space_before_class_lbrace = true +ij_coffeescript_space_before_comma = false +ij_coffeescript_space_before_function_left_parenth = true +ij_coffeescript_space_before_generator_mult = false +ij_coffeescript_space_before_property_colon = false +ij_coffeescript_space_before_type_colon = false +ij_coffeescript_space_before_unary_not = false +ij_coffeescript_spaces_around_additive_operators = true +ij_coffeescript_spaces_around_arrow_function_operator = true +ij_coffeescript_spaces_around_assignment_operators = true +ij_coffeescript_spaces_around_bitwise_operators = true +ij_coffeescript_spaces_around_equality_operators = true +ij_coffeescript_spaces_around_logical_operators = true +ij_coffeescript_spaces_around_multiplicative_operators = true +ij_coffeescript_spaces_around_relational_operators = true +ij_coffeescript_spaces_around_shift_operators = true +ij_coffeescript_spaces_around_unary_operator = false +ij_coffeescript_spaces_within_array_initializer_braces = false +ij_coffeescript_spaces_within_array_initializer_brackets = false +ij_coffeescript_spaces_within_imports = false +ij_coffeescript_spaces_within_index_brackets = false +ij_coffeescript_spaces_within_interpolation_expressions = false +ij_coffeescript_spaces_within_method_call_parentheses = false +ij_coffeescript_spaces_within_method_parentheses = false +ij_coffeescript_spaces_within_object_braces = false +ij_coffeescript_spaces_within_object_literal_braces = false +ij_coffeescript_spaces_within_object_type_braces = true +ij_coffeescript_spaces_within_range_brackets = false +ij_coffeescript_spaces_within_type_assertion = false +ij_coffeescript_spaces_within_union_types = true +ij_coffeescript_union_types_wrap = on_every_item +ij_coffeescript_use_chained_calls_group_indents = false +ij_coffeescript_use_double_quotes = true +ij_coffeescript_use_explicit_js_extension = global +ij_coffeescript_use_path_mapping = always +ij_coffeescript_use_public_modifier = false +ij_coffeescript_use_semicolon_after_statement = false +ij_coffeescript_var_declaration_wrap = normal + +[{*.jhm,*.rng,*.wsdl,*.fxml,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.xml}] +ij_xml_block_comment_at_first_column = true +ij_xml_keep_indents_on_empty_lines = false +ij_xml_line_comment_at_first_column = true + +[{*.nomad,*.hcl}] +indent_size = 2 +ij_hcl_align_property_on_equals = 2 +ij_hcl_align_property_on_value = 1 +ij_hcl_array_wrapping = 2 +ij_hcl_do_not_align_property = 0 +ij_hcl_keep_blank_lines_in_code = 2 +ij_hcl_keep_indents_on_empty_lines = false +ij_hcl_keep_line_breaks = true +ij_hcl_object_wrapping = 2 +ij_hcl_property_alignment = 0 +ij_hcl_property_line_commenter_character = 0 +ij_hcl_space_after_comma = true +ij_hcl_space_before_comma = false +ij_hcl_spaces_around_assignment_operators = true +ij_hcl_spaces_within_braces = false +ij_hcl_spaces_within_brackets = false +ij_hcl_wrap_long_lines = false + +[{*.sht,*.htm,*.html,*.shtm,*.shtml,*.ng}] +ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p +ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span,pre,textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.tf,*.tfvars}] +indent_size = 2 +ij_hcl-terraform_align_property_on_equals = 2 +ij_hcl-terraform_align_property_on_value = 1 +ij_hcl-terraform_array_wrapping = 2 +ij_hcl-terraform_do_not_align_property = 0 +ij_hcl-terraform_keep_blank_lines_in_code = 2 +ij_hcl-terraform_keep_indents_on_empty_lines = false +ij_hcl-terraform_keep_line_breaks = true +ij_hcl-terraform_object_wrapping = 2 +ij_hcl-terraform_property_alignment = 0 +ij_hcl-terraform_property_line_commenter_character = 0 +ij_hcl-terraform_space_after_comma = true +ij_hcl-terraform_space_before_comma = false +ij_hcl-terraform_spaces_around_assignment_operators = true +ij_hcl-terraform_spaces_within_braces = false +ij_hcl-terraform_spaces_within_brackets = false +ij_hcl-terraform_wrap_long_lines = false + +[{*.yml,*.yaml}] +indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true + +[{*.zsh,*.bash,*.sh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false + +[{.eslintrc,.babelrc,.prettierrc,jest.config,bowerrc,.stylelintrc,*.jsb3,*.jsb2,*.json}] indent_size = 2 +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false diff --git a/.env.example b/.env.example index 3ddf5414..7e64bd47 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ # # APPLICATION # -APP_NAME=express-typescript-boilerplate +APP_NAME=customized-express-typescript-boilerplate APP_SCHEMA=http APP_HOST=localhost APP_PORT=3000 @@ -14,31 +14,40 @@ APP_BANNER=true LOG_LEVEL=debug LOG_OUTPUT=dev +# +# AWS +# +AWS_ACCESS_KEY_ID=id +AWS_SECRET_ACCESS_KEY=secret +AWS_REGION=ap-northeast-2 + # # PostgreSQL DATABASE # -TYPEORM_CONNECTION=postgres -TYPEORM_HOST=localhost -TYPEORM_PORT=5432 -TYPEORM_USERNAME=username -TYPEORM_PASSWORD= -TYPEORM_DATABASE=my_database -TYPEORM_SYNCHRONIZE=false -TYPEORM_LOGGING=error -TYPEORM_LOGGER=advanced-console +#TYPEORM_ENABLED=true +#TYPEORM_CONNECTION=postgres +#TYPEORM_HOST=localhost +#TYPEORM_PORT=5432 +#TYPEORM_USERNAME=username +#TYPEORM_PASSWORD= +#TYPEORM_DATABASE=my_database +#TYPEORM_SYNCHRONIZE=false +#TYPEORM_LOGGING=error +#TYPEORM_LOGGER=advanced-console # # MySQL DATABASE # -# TYPEORM_CONNECTION=mysql -# TYPEORM_HOST=localhost -# TYPEORM_PORT=3306 -# TYPEORM_USERNAME=root -# TYPEORM_PASSWORD=root -# TYPEORM_DATABASE=my_database -# TYPEORM_SYNCHRONIZE=false -# TYPEORM_LOGGING=error -# TYPEORM_LOGGER=advanced-console +TYPEORM_ENABLED=true +TYPEORM_CONNECTION=mysql +TYPEORM_HOST=localhost +TYPEORM_PORT=3306 +TYPEORM_USERNAME=root +TYPEORM_PASSWORD=root +TYPEORM_DATABASE=customized-express-typescript-boilerplate +TYPEORM_SYNCHRONIZE=false +TYPEORM_LOGGING=error +TYPEORM_LOGGER=advanced-console # # PATH STRUCTRUE @@ -56,7 +65,7 @@ RESOLVERS=src/api/resolvers/**/*Resolver.ts # # GraphQL # -GRAPHQL_ENABLED=true +GRAPHQL_ENABLED=false GRAPHQL_ROUTE=/graphql GRAPHQL_EDITOR=true @@ -71,7 +80,7 @@ SWAGGER_PASSWORD=1234 # # Status Monitor # -MONITOR_ENABLED=true +MONITOR_ENABLED=false MONITOR_ROUTE=/monitor MONITOR_USERNAME=admin MONITOR_PASSWORD=1234 diff --git a/.env.test b/.env.test index 034901c0..94e883f7 100644 --- a/.env.test +++ b/.env.test @@ -14,9 +14,17 @@ APP_BANNER=false LOG_LEVEL=none LOG_OUTPUT=dev +# +# AWS +# +AWS_ACCESS_KEY_ID=id +AWS_SECRET_ACCESS_KEY=secret +AWS_REGION=ap-northeast-2 + # # DATABASE # +TYPEORM_ENABLED=true TYPEORM_CONNECTION=sqlite TYPEORM_DATABASE=./mydb.sql TYPEORM_LOGGING=error @@ -38,7 +46,7 @@ RESOLVERS=src/api/resolvers/**/*Resolver.ts # # GraphQL # -GRAPHQL_ENABLED=true +GRAPHQL_ENABLED=false GRAPHQL_ROUTE=/graphql GRAPHQL_EDITOR=false @@ -53,7 +61,7 @@ SWAGGER_PASSWORD=1234 # # Status Monitor # -MONITOR_ENABLED=true +MONITOR_ENABLED=false MONITOR_ROUTE=/monitor MONITOR_USERNAME=admin MONITOR_PASSWORD=1234 diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json deleted file mode 100644 index 53d65b75..00000000 --- a/.vscode/cSpell.json +++ /dev/null @@ -1,17 +0,0 @@ -// cSpell Settings -{ - // Version of the setting file. Always 0.1 - "version": "0.1", - // language - current active spelling language - "language": "en", - // words - list of words to be always considered correct - "words": [ - "hsts" - ], - // flagWords - list of words to be always considered incorrect - // This is useful for offensive words and common spelling errors. - // For example "hte" should be "the" - "flagWords": [ - "hte" - ] -} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 485cb437..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "recommendations": [ - "streetsidesoftware.code-spell-checker", - "eg2.tslint", - "EditorConfig.EditorConfig", - "christian-kohler.path-intellisense", - "mike-co.import-sorter", - "mikestead.dotenv", - "Orta.vscode-jest" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100755 index b4223bb8..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Debug", - "program": "${workspaceRoot}/dist/app.js", - "smartStep": true, - "outFiles": [ - "../dist/**/*.js" - ], - "protocol": "inspector", - "env": { - "NODE_ENV": "production" - } - }, - { - "type": "node", - "request": "attach", - "name": "Nodemon Debug", - "port": 9229, - "restart": true - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100755 index 348684a2..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "typescript.tsdk": "./node_modules/typescript/lib", - "cSpell.enabled": true, - "files.exclude": { - "tsconfig.build.json": true, - }, - "importSorter.generalConfiguration.sortOnBeforeSave": true, - "files.trimTrailingWhitespace": true, - "editor.formatOnSave": false -} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100755 index db09c576..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": "0.1.0", - "command": "npm", - "isShellCommand": true, - "suppressTaskName": true, - "tasks": [ - { - // Build task, Cmd+Shift+B - // "npm run build" - "taskName": "build", - "isBuildCommand": true, - "args": [ - "run", - "build" - ] - }, - { - // Test task, Cmd+Shift+T - // "npm test" - "taskName": "test", - "isTestCommand": true, - "args": [ - "test" - ] - } - ] -} diff --git a/Dockerfile b/Dockerfile index ccb424ef..7863a134 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,17 @@ -FROM node:alpine +FROM node:10.18.1 # Create work directory WORKDIR /usr/src/app -# Install runtime dependencies -RUN npm install yarn -g - # Copy app source to work directory COPY . /usr/src/app # Install app dependencies RUN yarn install -# Build and run the app -CMD npm start serve +# Build app +RUN npm start build + +# Run app +ENTRYPOINT ["npm", "start"] + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..0ea5b1c8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.7' +services: + app: + build: + dockerfile: ./Dockerfile + context: . + restart: always + container_name: app + volumes: + - ./.env:/app/.env + ports: + - "3000:3000" + diff --git a/package-scripts.js b/package-scripts.js index 6fa5661a..cfb8f9c3 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -2,298 +2,307 @@ * Windows: Please do not use trailing comma as windows will fail with token error */ -const { series, rimraf, } = require('nps-utils'); +const { series, rimraf } = require('nps-utils'); module.exports = { - scripts: { - default: 'nps start', - /** - * Starts the builded app from the dist directory. - */ - start: { - script: 'cross-env NODE_ENV=production node dist/app.js', - description: 'Starts the builded app', + scripts: { + default: 'nps start', + /** + * Starts the builded app from the dist directory. + */ + start: { + script: 'cross-env NODE_ENV=production node dist/app.js', + description: 'Starts the builded app', + }, + /** + * Serves the current app and watches for changes to restart it + */ + serve: { + inspector: { + script: series( + 'nps banner.serve', + 'nodemon --watch src --watch .env --inspect', + ), + description: 'Serves the current app and watches for changes to restart it, you may attach inspector to it.', + }, + script: series( + 'nps banner.serve', + 'nodemon --watch src --watch .env', + ), + description: 'Serves the current app and watches for changes to restart it', + }, + /** + * Setup of the development environment + */ + setup: { + script: series( + 'yarn install', + 'nps db.setup', + ), + description: 'Setup`s the development environment(yarn & database)', + }, + /** + * Creates the needed configuration files + */ + config: { + script: series( + runFast('./commands/tsconfig.ts'), + ), + hiddenFromHelp: true, + }, + /** + * Builds the app into the dist directory + */ + build: { + script: series( + 'nps banner.build', + 'nps config', + 'nps lint', + 'nps clean.dist', + 'nps transpile', + 'nps copy', + 'nps copy.tmp', + 'nps clean.tmp', + ), + description: 'Builds the app into the dist directory', + }, + /** + * Runs TSLint over your project + */ + lint: { + script: tslint(`./src/**/*.ts`), + hiddenFromHelp: true, + }, + /** + * Transpile your app into javascript + */ + transpile: { + script: `tsc --project ./tsconfig.build.json`, + hiddenFromHelp: true, + }, + /** + * Clean files and folders + */ + clean: { + default: { + script: series( + `nps banner.clean`, + `nps clean.dist`, + ), + description: 'Deletes the ./dist folder', + }, + dist: { + script: rimraf('./dist'), + hiddenFromHelp: true, + }, + tmp: { + script: rimraf('./.tmp'), + hiddenFromHelp: true, + }, + }, + /** + * Copies static files to the build folder + */ + copy: { + default: { + script: series( + `nps copy.public`, + ), + hiddenFromHelp: true, + }, + public: { + script: copy( + './src/public/*', + './dist', + ), + hiddenFromHelp: true, + }, + tmp: { + script: copyDir( + './.tmp/src', + './dist', + ), + hiddenFromHelp: true, + }, + }, + /** + * Database scripts + */ + db: { + generate: { + script: series( + 'nps banner.generate', + 'nps config', + runFast('./node_modules/typeorm/cli.js migration:generate -n ScriptGenerated'), + ), + description: 'Generates TypeORM migration files from Entity', + }, + migrate: { + script: series( + 'nps banner.migrate', + 'nps config', + runFast('./node_modules/typeorm/cli.js migration:run'), + ), + description: 'Migrates the database to newest version available', + }, + revert: { + script: series( + 'nps banner.revert', + 'nps config', + runFast('./node_modules/typeorm/cli.js migration:revert'), + ), + description: 'Downgrades the database', + }, + seed: { + script: series( + 'nps banner.seed', + 'nps config', + runFast('./commands/seed.ts'), + ), + description: 'Seeds generated records into the database', + }, + drop: { + script: runFast('./node_modules/typeorm/cli.js schema:drop'), + description: 'Drops the schema of the database', + }, + setup: { + script: series( + 'nps db.drop', + 'nps db.migrate', + 'nps db.seed', + ), + description: 'Recreates the database with seeded data', + }, + }, + /** + * These run various kinds of tests. Default is unit. + */ + test: { + default: 'nps test.unit', + unit: { + default: { + script: series( + 'nps banner.testUnit', + 'nps test.unit.pretest', + 'nps test.unit.run', + ), + description: 'Runs the unit tests', }, - /** - * Serves the current app and watches for changes to restart it - */ - serve: { - inspector: { - script: series( - 'nps banner.serve', - 'nodemon --watch src --watch .env --inspect' - ), - description: 'Serves the current app and watches for changes to restart it, you may attach inspector to it.' - }, - script: series( - 'nps banner.serve', - 'nodemon --watch src --watch .env' - ), - description: 'Serves the current app and watches for changes to restart it' + pretest: { + script: tslint(`./test/unit/**.ts`), + hiddenFromHelp: true, }, - /** - * Setup of the development environment - */ - setup: { - script: series( - 'yarn install', - 'nps db.setup', - ), - description: 'Setup`s the development environment(yarn & database)' + run: { + script: 'cross-env NODE_ENV=test jest --testPathPattern=unit', + hiddenFromHelp: true, }, - /** - * Creates the needed configuration files - */ - config: { - script: series( - runFast('./commands/tsconfig.ts'), - ), - hiddenFromHelp: true + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true, }, - /** - * Builds the app into the dist directory - */ - build: { - script: series( - 'nps banner.build', - 'nps config', - 'nps lint', - 'nps clean.dist', - 'nps transpile', - 'nps copy', - 'nps copy.tmp', - 'nps clean.tmp', - ), - description: 'Builds the app into the dist directory' + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true, }, - /** - * Runs TSLint over your project - */ - lint: { - script: tslint(`./src/**/*.ts`), - hiddenFromHelp: true + }, + integration: { + default: { + script: series( + 'nps banner.testIntegration', + 'nps test.integration.pretest', + 'nps test.integration.run', + ), + description: 'Runs the integration tests', }, - /** - * Transpile your app into javascript - */ - transpile: { - script: `tsc --project ./tsconfig.build.json`, - hiddenFromHelp: true + pretest: { + script: tslint(`./test/integration/**.ts`), + hiddenFromHelp: true, }, - /** - * Clean files and folders - */ - clean: { - default: { - script: series( - `nps banner.clean`, - `nps clean.dist` - ), - description: 'Deletes the ./dist folder' - }, - dist: { - script: rimraf('./dist'), - hiddenFromHelp: true - }, - tmp: { - script: rimraf('./.tmp'), - hiddenFromHelp: true - } + run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + script: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', + hiddenFromHelp: true, }, - /** - * Copies static files to the build folder - */ - copy: { - default: { - script: series( - `nps copy.public` - ), - hiddenFromHelp: true - }, - public: { - script: copy( - './src/public/*', - './dist' - ), - hiddenFromHelp: true - }, - tmp: { - script: copyDir( - './.tmp/src', - './dist' - ), - hiddenFromHelp: true - } + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true, }, - /** - * Database scripts - */ - db: { - migrate: { - script: series( - 'nps banner.migrate', - 'nps config', - runFast('./node_modules/typeorm/cli.js migration:run') - ), - description: 'Migrates the database to newest version available' - }, - revert: { - script: series( - 'nps banner.revert', - 'nps config', - runFast('./node_modules/typeorm/cli.js migration:revert') - ), - description: 'Downgrades the database' - }, - seed: { - script: series( - 'nps banner.seed', - 'nps config', - runFast('./commands/seed.ts') - ), - description: 'Seeds generated records into the database' - }, - drop: { - script: runFast('./node_modules/typeorm/cli.js schema:drop'), - description: 'Drops the schema of the database' - }, - setup: { - script: series( - 'nps db.drop', - 'nps db.migrate', - 'nps db.seed' - ), - description: 'Recreates the database with seeded data' - } + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true, }, - /** - * These run various kinds of tests. Default is unit. - */ - test: { - default: 'nps test.unit', - unit: { - default: { - script: series( - 'nps banner.testUnit', - 'nps test.unit.pretest', - 'nps test.unit.run' - ), - description: 'Runs the unit tests' - }, - pretest: { - script: tslint(`./test/unit/**.ts`), - hiddenFromHelp: true - }, - run: { - script: 'cross-env NODE_ENV=test jest --testPathPattern=unit', - hiddenFromHelp: true - }, - verbose: { - script: 'nps "test --verbose"', - hiddenFromHelp: true - }, - coverage: { - script: 'nps "test --coverage"', - hiddenFromHelp: true - } - }, - integration: { - default: { - script: series( - 'nps banner.testIntegration', - 'nps test.integration.pretest', - 'nps test.integration.run' - ), - description: 'Runs the integration tests' - }, - pretest: { - script: tslint(`./test/integration/**.ts`), - hiddenFromHelp: true - }, - run: { - // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - script: 'cross-env NODE_ENV=test jest --testPathPattern=integration -i', - hiddenFromHelp: true - }, - verbose: { - script: 'nps "test --verbose"', - hiddenFromHelp: true - }, - coverage: { - script: 'nps "test --coverage"', - hiddenFromHelp: true - } - }, - e2e: { - default: { - script: series( - 'nps banner.testE2E', - 'nps test.e2e.pretest', - 'nps test.e2e.run' - ), - description: 'Runs the e2e tests' - }, - pretest: { - script: tslint(`./test/e2e/**.ts`), - hiddenFromHelp: true - }, - run: { - // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. - script: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', - hiddenFromHelp: true - }, - verbose: { - script: 'nps "test --verbose"', - hiddenFromHelp: true - }, - coverage: { - script: 'nps "test --coverage"', - hiddenFromHelp: true - } - }, + }, + e2e: { + default: { + script: series( + 'nps banner.testE2E', + 'nps test.e2e.pretest', + 'nps test.e2e.run', + ), + description: 'Runs the e2e tests', }, - /** - * This creates pretty banner to the terminal - */ - banner: { - build: banner('build'), - serve: banner('serve'), - testUnit: banner('test.unit'), - testIntegration: banner('test.integration'), - testE2E: banner('test.e2e'), - migrate: banner('migrate'), - seed: banner('seed'), - revert: banner('revert'), - clean: banner('clean') - } - } + pretest: { + script: tslint(`./test/e2e/**.ts`), + hiddenFromHelp: true, + }, + run: { + // -i. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + script: 'cross-env NODE_ENV=test jest --testPathPattern=e2e -i', + hiddenFromHelp: true, + }, + verbose: { + script: 'nps "test --verbose"', + hiddenFromHelp: true, + }, + coverage: { + script: 'nps "test --coverage"', + hiddenFromHelp: true, + }, + }, + }, + /** + * This creates pretty banner to the terminal + */ + banner: { + build: banner('build'), + serve: banner('serve'), + testUnit: banner('test.unit'), + testIntegration: banner('test.integration'), + testE2E: banner('test.e2e'), + generate: banner('generate'), + migrate: banner('migrate'), + seed: banner('seed'), + revert: banner('revert'), + clean: banner('clean'), + }, + }, }; function banner(name) { - return { - hiddenFromHelp: true, - silent: true, - description: `Shows ${name} banners to the console`, - script: runFast(`./commands/banner.ts ${name}`), - }; + return { + hiddenFromHelp: true, + silent: true, + description: `Shows ${name} banners to the console`, + script: runFast(`./commands/banner.ts ${name}`), + }; } function copy(source, target) { - return `copyfiles --up 1 ${source} ${target}`; + return `copyfiles --up 1 ${source} ${target}`; } function copyDir(source, target) { - return `ncp ${source} ${target}`; + return `ncp ${source} ${target}`; } function run(path) { - return `ts-node ${path}`; + return `ts-node ${path}`; } function runFast(path) { - return `ts-node --transpileOnly ${path}`; + return `ts-node --transpileOnly ${path}`; } function tslint(path) { - return `tslint -c ./tslint.json ${path} --format stylish`; + return `tslint -c ./tslint.json ${path} --format stylish`; } diff --git a/package.json b/package.json index 5244e7e0..7a680aa8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "express-typescript-boilerplate", + "name": "customized-express-typescript-boilerplate", "version": "3.2.0", "description": "A delightful way to building a Node.js RESTful API Services with beautiful code written in TypeScript", "main": "src/app.ts", @@ -13,30 +13,17 @@ "engines": { "node": ">=8.0.0" }, - "repository": "git+ssh://git@github.com/w3tec/express-typescript-boilerplate.git", + "repository": "git+ssh://git@github.com:BlizzardBlue/customized-express-typescript-boilerplate.git", "keywords": [ "NodeJS", "TypeScript", "express", "boilerplate", "skeleton", - "starter-kit", - "w3tec.ch" - ], - "homepage": "https://github.com/w3tec/express-typescript-boilerplate#readme", - "author": "w3tec.ch ", - "contributors": [ - { - "name": "David Weber", - "email": "david.weber@w3tec.ch", - "url": "https://github.com/dweber019" - }, - { - "name": "Gery Hirschfeld", - "email": "gery.hirschfeld@w3tec.ch", - "url": "https://github.com/hirsch88" - } + "starter-kit" ], + "homepage": "https://github.com/BlizzardBlue/customized-express-typescript-boilerplate", + "author": "JiYun Kim ", "dependencies": { "bcrypt": "3.0.1", "chalk": "^2.4.1", @@ -79,13 +66,10 @@ "typeorm": "^0.2.5", "typeorm-seeding": "^1.0.0-beta.6", "typeorm-typedi-extensions": "^0.2.1", - "typescript": "^3.6.3", + "typescript": "^3.7.5", "uuid": "^3.3.2", "winston": "3.1.0" }, - "resolutions": { - "**/event-stream": "^4.0.1" - }, "jest": { "transform": { ".(ts|tsx)": "/test/preprocessor.js" @@ -100,7 +84,6 @@ "testEnvironment": "node", "setupTestFrameworkScriptFile": "./test/unit/lib/setup.ts" }, - "license": "MIT", "devDependencies": { "@types/bcrypt": "^2.0.0", "@types/bluebird": "^3.5.18", diff --git a/src/api/controllers/PetController.ts b/src/api/controllers/PetController.ts deleted file mode 100644 index afb02a66..00000000 --- a/src/api/controllers/PetController.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { IsNotEmpty, IsNumber, IsUUID, ValidateNested } from 'class-validator'; -import { - Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put -} from 'routing-controllers'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; - -import { PetNotFoundError } from '../errors/PetNotFoundError'; -import { Pet } from '../models/Pet'; -import { PetService } from '../services/PetService'; -import { UserResponse } from './UserController'; - -class BasePet { - @IsNotEmpty() - public name: string; - - @IsNumber() - public age: number; -} - -export class PetResponse extends BasePet { - @IsUUID() - public id: string; - - @ValidateNested() - public user: UserResponse; -} - -class CreatePetBody extends BasePet { - @IsUUID() - public userId: string; -} - -@Authorized() -@JsonController('/pets') -@OpenAPI({ security: [{ basicAuth: [] }] }) -export class PetController { - - constructor( - private petService: PetService - ) { } - - @Get() - @ResponseSchema(PetResponse, { isArray: true }) - public find(): Promise { - return this.petService.find(); - } - - @Get('/:id') - @OnUndefined(PetNotFoundError) - @ResponseSchema(PetResponse) - public one(@Param('id') id: string): Promise { - return this.petService.findOne(id); - } - - @Post() - @ResponseSchema(PetResponse) - public create(@Body({ required: true }) body: CreatePetBody): Promise { - const pet = new Pet(); - pet.age = body.age; - pet.name = body.name; - pet.userId = body.userId; - - return this.petService.create(pet); - } - - @Put('/:id') - @ResponseSchema(PetResponse) - public update(@Param('id') id: string, @Body() body: BasePet): Promise { - const pet = new Pet(); - pet.age = body.age; - pet.name = body.name; - - return this.petService.update(id, pet); - } - - @Delete('/:id') - public delete(@Param('id') id: string): Promise { - return this.petService.delete(id); - } - -} diff --git a/src/api/controllers/SamplePetController.ts b/src/api/controllers/SamplePetController.ts new file mode 100644 index 00000000..24173e2c --- /dev/null +++ b/src/api/controllers/SamplePetController.ts @@ -0,0 +1,82 @@ +import { IsNotEmpty, IsNumber, IsUUID, ValidateNested } from 'class-validator'; +import { + Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, +} from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; + +import { SamplePetNotFoundError } from '../errors/SamplePetNotFoundError'; +import { SamplePet } from '../models/SamplePet'; +import { SamplePetService } from '../services/SamplePetService'; +import { SampleUserResponse } from './SampleUserController'; + +class BasePet { + @IsNotEmpty() + public name: string; + + @IsNumber() + public age: number; +} + +export class SamplePetResponse extends BasePet { + @IsUUID() + public id: string; + + @ValidateNested() + public user: SampleUserResponse; +} + +class CreatePetBody extends BasePet { + @IsUUID() + public userId: string; +} + +@Authorized() +@JsonController('/samplePets') +@OpenAPI({ security: [{ basicAuth: [] }] }) +export class SamplePetController { + + constructor( + private petService: SamplePetService, + ) { + } + + @Get() + @ResponseSchema(SamplePetResponse, { isArray: true }) + public find(): Promise { + return this.petService.find(); + } + + @Get('/:id') + @OnUndefined(SamplePetNotFoundError) + @ResponseSchema(SamplePetResponse) + public one(@Param('id') id: string): Promise { + return this.petService.findOne(id); + } + + @Post() + @ResponseSchema(SamplePetResponse) + public create(@Body({ required: true }) body: CreatePetBody): Promise { + const pet = new SamplePet(); + pet.age = body.age; + pet.name = body.name; + pet.userId = body.userId; + + return this.petService.create(pet); + } + + @Put('/:id') + @ResponseSchema(SamplePetResponse) + public update(@Param('id') id: string, @Body() body: BasePet): Promise { + const pet = new SamplePet(); + pet.age = body.age; + pet.name = body.name; + + return this.petService.update(id, pet); + } + + @Delete('/:id') + public delete(@Param('id') id: string): Promise { + return this.petService.delete(id); + } + +} diff --git a/src/api/controllers/SampleUserController.ts b/src/api/controllers/SampleUserController.ts new file mode 100644 index 00000000..e4676809 --- /dev/null +++ b/src/api/controllers/SampleUserController.ts @@ -0,0 +1,101 @@ +import { Type } from 'class-transformer'; +import { IsEmail, IsNotEmpty, IsUUID, ValidateNested } from 'class-validator'; +import { + Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, Req, +} from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; + +import { SampleUserNotFoundError } from '../errors/SampleUserNotFoundError'; +import { SampleUser } from '../models/SampleUser'; +import { SampleUserService } from '../services/SampleUserService'; +import { SamplePetResponse } from './SamplePetController'; + +class SampleBaseUser { + @IsNotEmpty() + public firstName: string; + + @IsNotEmpty() + public lastName: string; + + @IsEmail() + @IsNotEmpty() + public email: string; + + @IsNotEmpty() + public username: string; +} + +export class SampleUserResponse extends SampleBaseUser { + @IsUUID() + public id: string; + + @ValidateNested({ each: true }) + @Type(() => SamplePetResponse) + public pets: SamplePetResponse[]; +} + +class SampleCreateUserBody extends SampleBaseUser { + @IsNotEmpty() + public password: string; +} + +@Authorized() +@JsonController('/sampleUsers') +@OpenAPI({ security: [{ basicAuth: [] }] }) +export class SampleUserController { + + constructor( + private userService: SampleUserService, + ) { + } + + @Get() + @ResponseSchema(SampleUserResponse, { isArray: true }) + public find(): Promise { + return this.userService.find(); + } + + @Get('/me') + @ResponseSchema(SampleUserResponse, { isArray: true }) + public findMe(@Req() req: any): Promise { + return req.user; + } + + @Get('/:id') + @OnUndefined(SampleUserNotFoundError) + @ResponseSchema(SampleUserResponse) + public one(@Param('id') id: string): Promise { + return this.userService.findOne(id); + } + + @Post() + @ResponseSchema(SampleUserResponse) + public create(@Body() body: SampleCreateUserBody): Promise { + const user = new SampleUser(); + user.email = body.email; + user.firstName = body.firstName; + user.lastName = body.lastName; + user.password = body.password; + user.username = body.username; + + return this.userService.create(user); + } + + @Put('/:id') + @ResponseSchema(SampleUserResponse) + public update(@Param('id') id: string, @Body() body: SampleBaseUser): Promise { + const user = new SampleUser(); + user.email = body.email; + user.firstName = body.firstName; + user.lastName = body.lastName; + user.username = body.username; + + return this.userService.update(id, user); + } + + @Delete('/:id') + public delete(@Param('id') id: string): Promise { + return this.userService.delete(id); + } + +} diff --git a/src/api/controllers/UserController.ts b/src/api/controllers/UserController.ts deleted file mode 100644 index eadbc3ea..00000000 --- a/src/api/controllers/UserController.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Type } from 'class-transformer'; -import { IsEmail, IsNotEmpty, IsUUID, ValidateNested } from 'class-validator'; -import { - Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, Req -} from 'routing-controllers'; -import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; - -import { UserNotFoundError } from '../errors/UserNotFoundError'; -import { User } from '../models/User'; -import { UserService } from '../services/UserService'; -import { PetResponse } from './PetController'; - -class BaseUser { - @IsNotEmpty() - public firstName: string; - - @IsNotEmpty() - public lastName: string; - - @IsEmail() - @IsNotEmpty() - public email: string; - - @IsNotEmpty() - public username: string; -} - -export class UserResponse extends BaseUser { - @IsUUID() - public id: string; - - @ValidateNested({ each: true }) - @Type(() => PetResponse) - public pets: PetResponse[]; -} - -class CreateUserBody extends BaseUser { - @IsNotEmpty() - public password: string; -} - -@Authorized() -@JsonController('/users') -@OpenAPI({ security: [{ basicAuth: [] }] }) -export class UserController { - - constructor( - private userService: UserService - ) { } - - @Get() - @ResponseSchema(UserResponse, { isArray: true }) - public find(): Promise { - return this.userService.find(); - } - - @Get('/me') - @ResponseSchema(UserResponse, { isArray: true }) - public findMe(@Req() req: any): Promise { - return req.user; - } - - @Get('/:id') - @OnUndefined(UserNotFoundError) - @ResponseSchema(UserResponse) - public one(@Param('id') id: string): Promise { - return this.userService.findOne(id); - } - - @Post() - @ResponseSchema(UserResponse) - public create(@Body() body: CreateUserBody): Promise { - const user = new User(); - user.email = body.email; - user.firstName = body.firstName; - user.lastName = body.lastName; - user.password = body.password; - user.username = body.username; - - return this.userService.create(user); - } - - @Put('/:id') - @ResponseSchema(UserResponse) - public update(@Param('id') id: string, @Body() body: BaseUser): Promise { - const user = new User(); - user.email = body.email; - user.firstName = body.firstName; - user.lastName = body.lastName; - user.username = body.username; - - return this.userService.update(id, user); - } - - @Delete('/:id') - public delete(@Param('id') id: string): Promise { - return this.userService.delete(id); - } - -} diff --git a/src/api/errors/PetNotFoundError.ts b/src/api/errors/PetNotFoundError.ts deleted file mode 100644 index 3f443512..00000000 --- a/src/api/errors/PetNotFoundError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { HttpError } from 'routing-controllers'; - -export class PetNotFoundError extends HttpError { - constructor() { - super(404, 'Pet not found!'); - } -} diff --git a/src/api/errors/SamplePetNotFoundError.ts b/src/api/errors/SamplePetNotFoundError.ts new file mode 100644 index 00000000..c0723d3b --- /dev/null +++ b/src/api/errors/SamplePetNotFoundError.ts @@ -0,0 +1,7 @@ +import { HttpError } from 'routing-controllers'; + +export class SamplePetNotFoundError extends HttpError { + constructor() { + super(404, 'SamplePet not found!'); + } +} diff --git a/src/api/errors/SampleUserNotFoundError.ts b/src/api/errors/SampleUserNotFoundError.ts new file mode 100644 index 00000000..6d88f347 --- /dev/null +++ b/src/api/errors/SampleUserNotFoundError.ts @@ -0,0 +1,7 @@ +import { HttpError } from 'routing-controllers'; + +export class SampleUserNotFoundError extends HttpError { + constructor() { + super(404, 'SampleUser not found!'); + } +} diff --git a/src/api/errors/UserNotFoundError.ts b/src/api/errors/UserNotFoundError.ts deleted file mode 100644 index 2a2b1371..00000000 --- a/src/api/errors/UserNotFoundError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { HttpError } from 'routing-controllers'; - -export class UserNotFoundError extends HttpError { - constructor() { - super(404, 'User not found!'); - } -} diff --git a/src/api/middlewares/CompressionMiddleware.ts b/src/api/middlewares/CompressionMiddleware.ts index c6c7d1c5..0e2c80e1 100644 --- a/src/api/middlewares/CompressionMiddleware.ts +++ b/src/api/middlewares/CompressionMiddleware.ts @@ -5,8 +5,8 @@ import { ExpressMiddlewareInterface, Middleware } from 'routing-controllers'; @Middleware({ type: 'before' }) export class CompressionMiddleware implements ExpressMiddlewareInterface { - public use(req: express.Request, res: express.Response, next: express.NextFunction): any { - return compression()(req, res, next); - } + public use(req: express.Request, res: express.Response, next: express.NextFunction): any { + return compression()(req, res, next); + } } diff --git a/src/api/middlewares/ErrorHandlerMiddleware.ts b/src/api/middlewares/ErrorHandlerMiddleware.ts index a1aa3e9d..5bc7210f 100644 --- a/src/api/middlewares/ErrorHandlerMiddleware.ts +++ b/src/api/middlewares/ErrorHandlerMiddleware.ts @@ -7,26 +7,27 @@ import { env } from '../../env'; @Middleware({ type: 'after' }) export class ErrorHandlerMiddleware implements ExpressErrorMiddlewareInterface { - public isProduction = env.isProduction; - - constructor( - @Logger(__filename) private log: LoggerInterface - ) { } - - public error(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction): void { - res.status(error.httpCode || 500); - res.json({ - name: error.name, - message: error.message, - errors: error[`errors`] || [], - }); - - if (this.isProduction) { - this.log.error(error.name, error.message); - } else { - this.log.error(error.name, error.stack); - } - + public isProduction = env.isProduction; + + constructor( + @Logger(__filename) private log: LoggerInterface, + ) { + } + + public error(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction): void { + res.status(error.httpCode || 500); + res.json({ + name: error.name, + message: error.message, + errors: error[`errors`] || [], + }); + + if (this.isProduction) { + this.log.error(error.name, error.message); + } else { + this.log.error(error.name, error.stack); } + } + } diff --git a/src/api/middlewares/LogMiddleware.ts b/src/api/middlewares/LogMiddleware.ts index 00a716a2..8d0d848f 100644 --- a/src/api/middlewares/LogMiddleware.ts +++ b/src/api/middlewares/LogMiddleware.ts @@ -8,14 +8,14 @@ import { Logger } from '../../lib/logger'; @Middleware({ type: 'before' }) export class LogMiddleware implements ExpressMiddlewareInterface { - private log = new Logger(__dirname); + private log = new Logger(__dirname); - public use(req: express.Request, res: express.Response, next: express.NextFunction): any { - return morgan(env.log.output, { - stream: { - write: this.log.info.bind(this.log), - }, - })(req, res, next); - } + public use(req: express.Request, res: express.Response, next: express.NextFunction): any { + return morgan(env.log.output, { + stream: { + write: this.log.info.bind(this.log), + }, + })(req, res, next); + } } diff --git a/src/api/middlewares/SecurityHstsMiddleware.ts b/src/api/middlewares/SecurityHstsMiddleware.ts index 3a0a9a3c..d2095e2b 100644 --- a/src/api/middlewares/SecurityHstsMiddleware.ts +++ b/src/api/middlewares/SecurityHstsMiddleware.ts @@ -5,11 +5,11 @@ import { ExpressMiddlewareInterface, Middleware } from 'routing-controllers'; @Middleware({ type: 'before' }) export class SecurityHstsMiddleware implements ExpressMiddlewareInterface { - public use(req: express.Request, res: express.Response, next: express.NextFunction): any { - return helmet.hsts({ - maxAge: 31536000, - includeSubdomains: true, - })(req, res, next); - } + public use(req: express.Request, res: express.Response, next: express.NextFunction): any { + return helmet.hsts({ + maxAge: 31536000, + includeSubdomains: true, + })(req, res, next); + } } diff --git a/src/api/middlewares/SecurityMiddleware.ts b/src/api/middlewares/SecurityMiddleware.ts index 45900029..1539e845 100644 --- a/src/api/middlewares/SecurityMiddleware.ts +++ b/src/api/middlewares/SecurityMiddleware.ts @@ -5,8 +5,8 @@ import { ExpressMiddlewareInterface, Middleware } from 'routing-controllers'; @Middleware({ type: 'before' }) export class SecurityMiddleware implements ExpressMiddlewareInterface { - public use(req: express.Request, res: express.Response, next: express.NextFunction): any { - return helmet()(req, res, next); - } + public use(req: express.Request, res: express.Response, next: express.NextFunction): any { + return helmet()(req, res, next); + } } diff --git a/src/api/middlewares/SecurityNoCacheMiddleware.ts b/src/api/middlewares/SecurityNoCacheMiddleware.ts index 7f953b3d..45b09786 100644 --- a/src/api/middlewares/SecurityNoCacheMiddleware.ts +++ b/src/api/middlewares/SecurityNoCacheMiddleware.ts @@ -5,8 +5,8 @@ import { ExpressMiddlewareInterface, Middleware } from 'routing-controllers'; @Middleware({ type: 'before' }) export class SecurityNoCacheMiddleware implements ExpressMiddlewareInterface { - public use(req: express.Request, res: express.Response, next: express.NextFunction): any { - return helmet.noCache()(req, res, next); - } + public use(req: express.Request, res: express.Response, next: express.NextFunction): any { + return helmet.noCache()(req, res, next); + } } diff --git a/src/api/models/Pet.ts b/src/api/models/Pet.ts deleted file mode 100644 index f25eb9ff..00000000 --- a/src/api/models/Pet.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IsNotEmpty } from 'class-validator'; -import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; - -import { User } from './User'; - -@Entity() -export class Pet { - - @PrimaryColumn('uuid') - public id: string; - - @IsNotEmpty() - @Column() - public name: string; - - @IsNotEmpty() - @Column() - public age: number; - - @Column({ - name: 'user_id', - nullable: true, - }) - public userId: string; - - @ManyToOne(type => User, user => user.pets) - @JoinColumn({ name: 'user_id' }) - public user: User; - - public toString(): string { - return `${this.name}`; - } - -} diff --git a/src/api/models/SamplePet.ts b/src/api/models/SamplePet.ts new file mode 100644 index 00000000..63914eab --- /dev/null +++ b/src/api/models/SamplePet.ts @@ -0,0 +1,34 @@ +import { IsNotEmpty } from 'class-validator'; +import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm'; + +import { SampleUser } from './SampleUser'; + +@Entity() +export class SamplePet { + + @PrimaryColumn('uuid') + public id: string; + + @IsNotEmpty() + @Column() + public name: string; + + @IsNotEmpty() + @Column() + public age: number; + + @Column({ + name: 'user_id', + nullable: true, + }) + public userId: string; + + @ManyToOne(type => SampleUser, user => user.pets) + @JoinColumn({ name: 'user_id' }) + public user: SampleUser; + + public toString(): string { + return `${this.name}`; + } + +} diff --git a/src/api/models/SampleUser.ts b/src/api/models/SampleUser.ts new file mode 100644 index 00000000..96e53ec5 --- /dev/null +++ b/src/api/models/SampleUser.ts @@ -0,0 +1,66 @@ +import * as bcrypt from 'bcrypt'; +import { Exclude } from 'class-transformer'; +import { IsNotEmpty } from 'class-validator'; +import { BeforeInsert, Column, Entity, OneToMany, PrimaryColumn } from 'typeorm'; + +import { SamplePet } from './SamplePet'; + +@Entity() +export class SampleUser { + + public static hashPassword(password: string): Promise { + return new Promise((resolve, reject) => { + bcrypt.hash(password, 10, (err, hash) => { + if (err) { + return reject(err); + } + resolve(hash); + }); + }); + } + + public static comparePassword(user: SampleUser, password: string): Promise { + return new Promise((resolve, reject) => { + bcrypt.compare(password, user.password, (err, res) => { + resolve(res === true); + }); + }); + } + + @PrimaryColumn('uuid') + public id: string; + + @IsNotEmpty() + @Column({ name: 'first_name' }) + public firstName: string; + + @IsNotEmpty() + @Column({ name: 'last_name' }) + public lastName: string; + + @IsNotEmpty() + @Column() + public email: string; + + @IsNotEmpty() + @Column() + @Exclude() + public password: string; + + @IsNotEmpty() + @Column() + public username: string; + + @OneToMany(type => SamplePet, pet => pet.user) + public pets: SamplePet[]; + + public toString(): string { + return `${this.firstName} ${this.lastName} (${this.email})`; + } + + @BeforeInsert() + public async hashPassword(): Promise { + this.password = await SampleUser.hashPassword(this.password); + } + +} diff --git a/src/api/models/User.ts b/src/api/models/User.ts deleted file mode 100644 index c78d21f6..00000000 --- a/src/api/models/User.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as bcrypt from 'bcrypt'; -import { Exclude } from 'class-transformer'; -import { IsNotEmpty } from 'class-validator'; -import { BeforeInsert, Column, Entity, OneToMany, PrimaryColumn } from 'typeorm'; - -import { Pet } from './Pet'; - -@Entity() -export class User { - - public static hashPassword(password: string): Promise { - return new Promise((resolve, reject) => { - bcrypt.hash(password, 10, (err, hash) => { - if (err) { - return reject(err); - } - resolve(hash); - }); - }); - } - - public static comparePassword(user: User, password: string): Promise { - return new Promise((resolve, reject) => { - bcrypt.compare(password, user.password, (err, res) => { - resolve(res === true); - }); - }); - } - - @PrimaryColumn('uuid') - public id: string; - - @IsNotEmpty() - @Column({ name: 'first_name' }) - public firstName: string; - - @IsNotEmpty() - @Column({ name: 'last_name' }) - public lastName: string; - - @IsNotEmpty() - @Column() - public email: string; - - @IsNotEmpty() - @Column() - @Exclude() - public password: string; - - @IsNotEmpty() - @Column() - public username: string; - - @OneToMany(type => Pet, pet => pet.user) - public pets: Pet[]; - - public toString(): string { - return `${this.firstName} ${this.lastName} (${this.email})`; - } - - @BeforeInsert() - public async hashPassword(): Promise { - this.password = await User.hashPassword(this.password); - } - -} diff --git a/src/api/repositories/PetRepository.ts b/src/api/repositories/PetRepository.ts deleted file mode 100644 index cfe26577..00000000 --- a/src/api/repositories/PetRepository.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EntityRepository, Repository } from 'typeorm'; - -import { Pet } from '../models/Pet'; - -@EntityRepository(Pet) -export class PetRepository extends Repository { - - /** - * Find by user_id is used for our data-loader to get all needed pets in one query. - */ - public findByUserIds(ids: string[]): Promise { - return this.createQueryBuilder() - .select() - .where(`pet.user_id IN (${ids.map(id => `'${id}'`).join(', ')})`) - .getMany(); - } - -} diff --git a/src/api/repositories/SamplePetRepository.ts b/src/api/repositories/SamplePetRepository.ts new file mode 100644 index 00000000..9cb489c0 --- /dev/null +++ b/src/api/repositories/SamplePetRepository.ts @@ -0,0 +1,18 @@ +import { EntityRepository, Repository } from 'typeorm'; + +import { SamplePet } from '../models/SamplePet'; + +@EntityRepository(SamplePet) +export class SamplePetRepository extends Repository { + + /** + * Find by user_id is used for our data-loader to get all needed pets in one query. + */ + public findByUserIds(ids: string[]): Promise { + return this.createQueryBuilder() + .select() + .where(`pet.user_id IN (${ids.map(id => `'${id}'`).join(', ')})`) + .getMany(); + } + +} diff --git a/src/api/repositories/SampleUserRepository.ts b/src/api/repositories/SampleUserRepository.ts new file mode 100644 index 00000000..19765c73 --- /dev/null +++ b/src/api/repositories/SampleUserRepository.ts @@ -0,0 +1,8 @@ +import { EntityRepository, Repository } from 'typeorm'; + +import { SampleUser } from '../models/SampleUser'; + +@EntityRepository(SampleUser) +export class SampleUserRepository extends Repository { + +} diff --git a/src/api/repositories/UserRepository.ts b/src/api/repositories/UserRepository.ts deleted file mode 100644 index 469182f3..00000000 --- a/src/api/repositories/UserRepository.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { EntityRepository, Repository } from 'typeorm'; - -import { User } from '../models/User'; - -@EntityRepository(User) -export class UserRepository extends Repository { - -} diff --git a/src/api/resolvers/PetResolver.ts b/src/api/resolvers/PetResolver.ts deleted file mode 100644 index 78324bf2..00000000 --- a/src/api/resolvers/PetResolver.ts +++ /dev/null @@ -1,54 +0,0 @@ -import DataLoader from 'dataloader'; -import { Arg, Ctx, FieldResolver, Mutation, Query, Resolver, Root } from 'type-graphql'; -import { Service } from 'typedi'; - -import { DLoader } from '../../decorators/DLoader'; -import { Logger, LoggerInterface } from '../../decorators/Logger'; -import { Context } from '../Context'; -import { Pet as PetModel } from '../models/Pet'; -import { User as UserModel } from '../models/User'; -import { PetService } from '../services/PetService'; -import { PetInput } from '../types/input/PetInput'; -import { Pet } from '../types/Pet'; - -@Service() -@Resolver(of => Pet) -export class PetResolver { - - constructor( - private petService: PetService, - @Logger(__filename) private log: LoggerInterface, - @DLoader(UserModel) private userLoader: DataLoader - ) { } - - @Query(returns => [Pet]) - public pets(@Ctx() { requestId }: Context): Promise { - this.log.info(`{${requestId}} Find all users`); - return this.petService.find(); - } - - @Mutation(returns => Pet) - public async addPet(@Arg('pet') pet: PetInput): Promise { - const newPet = new PetModel(); - newPet.name = pet.name; - newPet.age = pet.age; - return this.petService.create(newPet); - } - - @FieldResolver() - public async owner(@Root() pet: PetModel): Promise { - if (pet.userId) { - return this.userLoader.load(pet.userId); - } - // return this.userService.findOne(`${pet.userId}`); - } - - // user: createDataLoader(UserRepository), - - // petsByUserIds: createDataLoader(PetRepository, { - // method: 'findByUserIds', - // key: 'userId', - // multiple: true, - // }), - -} diff --git a/src/api/resolvers/SamplePetResolver.ts b/src/api/resolvers/SamplePetResolver.ts new file mode 100644 index 00000000..9f1fbff4 --- /dev/null +++ b/src/api/resolvers/SamplePetResolver.ts @@ -0,0 +1,55 @@ +import DataLoader from 'dataloader'; +import { Arg, Ctx, FieldResolver, Mutation, Query, Resolver, Root } from 'type-graphql'; +import { Service } from 'typedi'; + +import { DLoader } from '../../decorators/DLoader'; +import { Logger, LoggerInterface } from '../../decorators/Logger'; +import { Context } from '../Context'; +import { SamplePet as PetModel } from '../models/SamplePet'; +import { SampleUser as UserModel } from '../models/SampleUser'; +import { SamplePetService } from '../services/SamplePetService'; +import { SamplePetInput } from '../types/input/SamplePetInput'; +import { SamplePet } from '../types/SamplePet'; + +@Service() +@Resolver(of => SamplePet) +export class SamplePetResolver { + + constructor( + private petService: SamplePetService, + @Logger(__filename) private log: LoggerInterface, + @DLoader(UserModel) private userLoader: DataLoader, + ) { + } + + @Query(returns => [SamplePet]) + public pets(@Ctx() { requestId }: Context): Promise { + this.log.info(`{${requestId}} Find all users`); + return this.petService.find(); + } + + @Mutation(returns => SamplePet) + public async addPet(@Arg('pet') pet: SamplePetInput): Promise { + const newPet = new PetModel(); + newPet.name = pet.name; + newPet.age = pet.age; + return this.petService.create(newPet); + } + + @FieldResolver() + public async owner(@Root() pet: PetModel): Promise { + if (pet.userId) { + return this.userLoader.load(pet.userId); + } + // return this.userService.findOne(`${pet.userId}`); + } + + // user: createDataLoader(SampleUserRepository), + + // petsByUserIds: createDataLoader(SamplePetRepository, { + // method: 'findByUserIds', + // key: 'userId', + // multiple: true, + // }), + +} diff --git a/src/api/resolvers/SampleUserResolver.ts b/src/api/resolvers/SampleUserResolver.ts new file mode 100644 index 00000000..0eea116a --- /dev/null +++ b/src/api/resolvers/SampleUserResolver.ts @@ -0,0 +1,29 @@ +import { FieldResolver, Query, Resolver, Root } from 'type-graphql'; +import { Service } from 'typedi'; + +import { SampleUser as UserModel } from '../models/SampleUser'; +import { SamplePetService } from '../services/SamplePetService'; +import { SampleUserService } from '../services/SampleUserService'; +import { SampleUser } from '../types/SampleUser'; + +@Service() +@Resolver(of => SampleUser) +export class SampleUserResolver { + + constructor( + private userService: SampleUserService, + private petService: SamplePetService, + ) { + } + + @Query(returns => [SampleUser]) + public users(): Promise { + return this.userService.find(); + } + + @FieldResolver() + public async pets(@Root() user: UserModel): Promise { + return this.petService.findByUser(user); + } + +} diff --git a/src/api/resolvers/UserResolver.ts b/src/api/resolvers/UserResolver.ts deleted file mode 100644 index 64373499..00000000 --- a/src/api/resolvers/UserResolver.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { FieldResolver, Query, Resolver, Root } from 'type-graphql'; -import { Service } from 'typedi'; - -import { User as UserModel } from '../models/User'; -import { PetService } from '../services/PetService'; -import { UserService } from '../services/UserService'; -import { User } from '../types/User'; - -@Service() -@Resolver(of => User) -export class UserResolver { - - constructor( - private userService: UserService, - private petService: PetService - ) {} - - @Query(returns => [User]) - public users(): Promise { - return this.userService.find(); - } - - @FieldResolver() - public async pets(@Root() user: UserModel): Promise { - return this.petService.findByUser(user); - } - -} diff --git a/src/api/services/PetService.ts b/src/api/services/PetService.ts deleted file mode 100644 index 86011a78..00000000 --- a/src/api/services/PetService.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Service } from 'typedi'; -import { OrmRepository } from 'typeorm-typedi-extensions'; -import uuid from 'uuid'; - -import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher'; -import { Logger, LoggerInterface } from '../../decorators/Logger'; -import { Pet } from '../models/Pet'; -import { User } from '../models/User'; -import { PetRepository } from '../repositories/PetRepository'; -import { events } from '../subscribers/events'; - -@Service() -export class PetService { - - constructor( - @OrmRepository() private petRepository: PetRepository, - @EventDispatcher() private eventDispatcher: EventDispatcherInterface, - @Logger(__filename) private log: LoggerInterface - ) { } - - public find(): Promise { - this.log.info('Find all pets'); - return this.petRepository.find(); - } - - public findByUser(user: User): Promise { - this.log.info('Find all pets of the user', user.toString()); - return this.petRepository.find({ - where: { - userId: user.id, - }, - }); - } - - public findOne(id: string): Promise { - this.log.info('Find all pets'); - return this.petRepository.findOne({ id }); - } - - public async create(pet: Pet): Promise { - this.log.info('Create a new pet => ', pet.toString()); - pet.id = uuid.v1(); - const newPet = await this.petRepository.save(pet); - this.eventDispatcher.dispatch(events.pet.created, newPet); - return newPet; - } - - public update(id: string, pet: Pet): Promise { - this.log.info('Update a pet'); - pet.id = id; - return this.petRepository.save(pet); - } - - public async delete(id: string): Promise { - this.log.info('Delete a pet'); - await this.petRepository.delete(id); - return; - } - -} diff --git a/src/api/services/SamplePetService.ts b/src/api/services/SamplePetService.ts new file mode 100644 index 00000000..0280b787 --- /dev/null +++ b/src/api/services/SamplePetService.ts @@ -0,0 +1,61 @@ +import { Service } from 'typedi'; +import { OrmRepository } from 'typeorm-typedi-extensions'; +import uuid from 'uuid'; + +import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher'; +import { Logger, LoggerInterface } from '../../decorators/Logger'; +import { SamplePet } from '../models/SamplePet'; +import { SampleUser } from '../models/SampleUser'; +import { SamplePetRepository } from '../repositories/SamplePetRepository'; +import { events } from '../subscribers/events'; + +@Service() +export class SamplePetService { + + constructor( + @OrmRepository() private petRepository: SamplePetRepository, + @EventDispatcher() private eventDispatcher: EventDispatcherInterface, + @Logger(__filename) private log: LoggerInterface, + ) { + } + + public find(): Promise { + this.log.info('Find all pets'); + return this.petRepository.find(); + } + + public findByUser(user: SampleUser): Promise { + this.log.info('Find all pets of the user', user.toString()); + return this.petRepository.find({ + where: { + userId: user.id, + }, + }); + } + + public findOne(id: string): Promise { + this.log.info('Find all pets'); + return this.petRepository.findOne({ id }); + } + + public async create(pet: SamplePet): Promise { + this.log.info('Create a new pet => ', pet.toString()); + pet.id = uuid.v1(); + const newPet = await this.petRepository.save(pet); + this.eventDispatcher.dispatch(events.samplePet.created, newPet); + return newPet; + } + + public update(id: string, pet: SamplePet): Promise { + this.log.info('Update a pet'); + pet.id = id; + return this.petRepository.save(pet); + } + + public async delete(id: string): Promise { + this.log.info('Delete a pet'); + await this.petRepository.delete(id); + return; + } + +} diff --git a/src/api/services/SampleUserService.ts b/src/api/services/SampleUserService.ts new file mode 100644 index 00000000..2543cb49 --- /dev/null +++ b/src/api/services/SampleUserService.ts @@ -0,0 +1,51 @@ +import { Service } from 'typedi'; +import { OrmRepository } from 'typeorm-typedi-extensions'; +import uuid from 'uuid'; + +import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher'; +import { Logger, LoggerInterface } from '../../decorators/Logger'; +import { SampleUser } from '../models/SampleUser'; +import { SampleUserRepository } from '../repositories/SampleUserRepository'; +import { events } from '../subscribers/events'; + +@Service() +export class SampleUserService { + + constructor( + @OrmRepository() private userRepository: SampleUserRepository, + @EventDispatcher() private eventDispatcher: EventDispatcherInterface, + @Logger(__filename) private log: LoggerInterface, + ) { + } + + public find(): Promise { + this.log.info('Find all users'); + return this.userRepository.find({ relations: ['pets'] }); + } + + public findOne(id: string): Promise { + this.log.info('Find one user'); + return this.userRepository.findOne({ id }); + } + + public async create(user: SampleUser): Promise { + this.log.info('Create a new user => ', user.toString()); + user.id = uuid.v1(); + const newUser = await this.userRepository.save(user); + this.eventDispatcher.dispatch(events.sampleUser.created, newUser); + return newUser; + } + + public update(id: string, user: SampleUser): Promise { + this.log.info('Update a user'); + user.id = id; + return this.userRepository.save(user); + } + + public async delete(id: string): Promise { + this.log.info('Delete a user'); + await this.userRepository.delete(id); + return; + } + +} diff --git a/src/api/services/UserService.ts b/src/api/services/UserService.ts deleted file mode 100644 index 702b7351..00000000 --- a/src/api/services/UserService.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Service } from 'typedi'; -import { OrmRepository } from 'typeorm-typedi-extensions'; -import uuid from 'uuid'; - -import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher'; -import { Logger, LoggerInterface } from '../../decorators/Logger'; -import { User } from '../models/User'; -import { UserRepository } from '../repositories/UserRepository'; -import { events } from '../subscribers/events'; - -@Service() -export class UserService { - - constructor( - @OrmRepository() private userRepository: UserRepository, - @EventDispatcher() private eventDispatcher: EventDispatcherInterface, - @Logger(__filename) private log: LoggerInterface - ) { } - - public find(): Promise { - this.log.info('Find all users'); - return this.userRepository.find({ relations: ['pets'] }); - } - - public findOne(id: string): Promise { - this.log.info('Find one user'); - return this.userRepository.findOne({ id }); - } - - public async create(user: User): Promise { - this.log.info('Create a new user => ', user.toString()); - user.id = uuid.v1(); - const newUser = await this.userRepository.save(user); - this.eventDispatcher.dispatch(events.user.created, newUser); - return newUser; - } - - public update(id: string, user: User): Promise { - this.log.info('Update a user'); - user.id = id; - return this.userRepository.save(user); - } - - public async delete(id: string): Promise { - this.log.info('Delete a user'); - await this.userRepository.delete(id); - return; - } - -} diff --git a/src/api/subscribers/SampleUserEventSubscriber.ts b/src/api/subscribers/SampleUserEventSubscriber.ts new file mode 100644 index 00000000..09c15b72 --- /dev/null +++ b/src/api/subscribers/SampleUserEventSubscriber.ts @@ -0,0 +1,17 @@ +import { EventSubscriber, On } from 'event-dispatch'; + +import { Logger } from '../../lib/logger'; +import { SampleUser } from '../models/SampleUser'; +import { events } from './events'; + +const log = new Logger(__filename); + +@EventSubscriber() +export class SampleUserEventSubscriber { + + @On(events.sampleUser.created) + public onSampleUserCreate(user: SampleUser): void { + log.info('User ' + user.toString() + ' created!'); + } + +} diff --git a/src/api/subscribers/UserEventSubscriber.ts b/src/api/subscribers/UserEventSubscriber.ts deleted file mode 100644 index 1c25f3e2..00000000 --- a/src/api/subscribers/UserEventSubscriber.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { EventSubscriber, On } from 'event-dispatch'; - -import { Logger } from '../../lib/logger'; -import { User } from '../models/User'; -import { events } from './events'; - -const log = new Logger(__filename); - -@EventSubscriber() -export class UserEventSubscriber { - - @On(events.user.created) - public onUserCreate(user: User): void { - log.info('User ' + user.toString() + ' created!'); - } - -} diff --git a/src/api/subscribers/events.ts b/src/api/subscribers/events.ts index d9b20bf3..445ba296 100644 --- a/src/api/subscribers/events.ts +++ b/src/api/subscribers/events.ts @@ -4,10 +4,10 @@ * Define all your possible custom events here. */ export const events = { - user: { - created: 'onUserCreate', - }, - pet: { - created: 'onPetCreate', - }, + sampleUser: { + created: 'onSampleUserCreate', + }, + samplePet: { + created: 'onSamplePetCreate', + }, }; diff --git a/src/api/types/Pet.ts b/src/api/types/Pet.ts deleted file mode 100644 index d10c5efa..00000000 --- a/src/api/types/Pet.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Field, ID, Int, ObjectType } from 'type-graphql'; - -import { User } from './User'; - -@ObjectType({ - description: 'Pet object.', -}) -export class Pet { - - @Field(type => ID) - public id: string; - - @Field({ - description: 'The name of the pet.', - }) - public name: string; - - @Field(type => Int, { - description: 'The age of the pet in years.', - }) - public age: number; - - @Field(type => User, { - nullable: true, - }) - public owner: User; - -} diff --git a/src/api/types/SamplePet.ts b/src/api/types/SamplePet.ts new file mode 100644 index 00000000..c57c22f3 --- /dev/null +++ b/src/api/types/SamplePet.ts @@ -0,0 +1,28 @@ +import { Field, ID, Int, ObjectType } from 'type-graphql'; + +import { SampleUser } from './SampleUser'; + +@ObjectType({ + description: 'SamplePet object.', +}) +export class SamplePet { + + @Field(type => ID) + public id: string; + + @Field({ + description: 'The name of the pet.', + }) + public name: string; + + @Field(type => Int, { + description: 'The age of the pet in years.', + }) + public age: number; + + @Field(type => SampleUser, { + nullable: true, + }) + public owner: SampleUser; + +} diff --git a/src/api/types/SampleUser.ts b/src/api/types/SampleUser.ts new file mode 100644 index 00000000..500871f0 --- /dev/null +++ b/src/api/types/SampleUser.ts @@ -0,0 +1,33 @@ +import { Field, ID, ObjectType } from 'type-graphql'; + +import { SamplePet } from './SamplePet'; + +@ObjectType({ + description: 'SampleUser object.', +}) +export class SampleUser { + + @Field(type => ID) + public id: string; + + @Field({ + description: 'The first name of the user.', + }) + public firstName: string; + + @Field({ + description: 'The last name of the user.', + }) + public lastName: string; + + @Field({ + description: 'The email of the user.', + }) + public email: string; + + @Field(type => [SamplePet], { + description: 'A list of pets which belong to the user.', + }) + public pets: SamplePet[]; + +} diff --git a/src/api/types/User.ts b/src/api/types/User.ts deleted file mode 100644 index f19aa52c..00000000 --- a/src/api/types/User.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Field, ID, ObjectType } from 'type-graphql'; - -import { Pet } from './Pet'; - -@ObjectType({ - description: 'User object.', -}) -export class User { - - @Field(type => ID) - public id: string; - - @Field({ - description: 'The first name of the user.', - }) - public firstName: string; - - @Field({ - description: 'The last name of the user.', - }) - public lastName: string; - - @Field({ - description: 'The email of the user.', - }) - public email: string; - - @Field(type => [Pet], { - description: 'A list of pets which belong to the user.', - }) - public pets: Pet[]; - -} diff --git a/src/api/types/input/PetInput.ts b/src/api/types/input/PetInput.ts deleted file mode 100644 index ee370440..00000000 --- a/src/api/types/input/PetInput.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Field, InputType, Int } from 'type-graphql'; - -import { Pet } from '../Pet'; - -@InputType() -export class PetInput implements Partial { - - @Field() - public name: string; - - @Field(type => Int, { - description: 'The age of the pet in years.', - }) - public age: number; - -} diff --git a/src/api/types/input/SamplePetInput.ts b/src/api/types/input/SamplePetInput.ts new file mode 100644 index 00000000..96921045 --- /dev/null +++ b/src/api/types/input/SamplePetInput.ts @@ -0,0 +1,16 @@ +import { Field, InputType, Int } from 'type-graphql'; + +import { SamplePet } from '../SamplePet'; + +@InputType() +export class SamplePetInput implements Partial { + + @Field() + public name: string; + + @Field(type => Int, { + description: 'The age of the pet in years.', + }) + public age: number; + +} diff --git a/src/app.ts b/src/app.ts index 78503ca4..a4dfa733 100644 --- a/src/app.ts +++ b/src/app.ts @@ -26,22 +26,22 @@ import { winstonLoader } from './loaders/winstonLoader'; const log = new Logger(__filename); bootstrapMicroframework({ - /** - * Loader is a place where you can configure all your modules during microframework - * bootstrap process. All loaders are executed one by one in a sequential order. - */ - loaders: [ - winstonLoader, - iocLoader, - eventDispatchLoader, - typeormLoader, - expressLoader, - swaggerLoader, - monitorLoader, - homeLoader, - publicLoader, - graphqlLoader, - ], + /** + * Loader is a place where you can configure all your modules during microframework + * bootstrap process. All loaders are executed one by one in a sequential order. + */ + loaders: [ + winstonLoader, + iocLoader, + eventDispatchLoader, + typeormLoader, + expressLoader, + swaggerLoader, + monitorLoader, + homeLoader, + publicLoader, + graphqlLoader, + ], }) - .then(() => banner(log)) - .catch(error => log.error('Application is crashed: ' + error)); + .then(() => banner(log)) + .catch(error => log.error('Application is crashed: ' + error)); diff --git a/src/auth/AuthService.ts b/src/auth/AuthService.ts index 296372da..c20aafe3 100644 --- a/src/auth/AuthService.ts +++ b/src/auth/AuthService.ts @@ -2,47 +2,48 @@ import * as express from 'express'; import { Service } from 'typedi'; import { OrmRepository } from 'typeorm-typedi-extensions'; -import { User } from '../api/models/User'; -import { UserRepository } from '../api/repositories/UserRepository'; +import { SampleUser } from '../api/models/SampleUser'; +import { SampleUserRepository } from '../api/repositories/SampleUserRepository'; import { Logger, LoggerInterface } from '../decorators/Logger'; @Service() export class AuthService { - constructor( - @Logger(__filename) private log: LoggerInterface, - @OrmRepository() private userRepository: UserRepository - ) { } - - public parseBasicAuthFromRequest(req: express.Request): { username: string, password: string } { - const authorization = req.header('authorization'); - - if (authorization && authorization.split(' ')[0] === 'Basic') { - this.log.info('Credentials provided by the client'); - const decodedBase64 = Buffer.from(authorization.split(' ')[1], 'base64').toString('ascii'); - const username = decodedBase64.split(':')[0]; - const password = decodedBase64.split(':')[1]; - if (username && password) { - return { username, password }; - } - } - - this.log.info('No credentials provided by the client'); - return undefined; + constructor( + @Logger(__filename) private log: LoggerInterface, + @OrmRepository() private userRepository: SampleUserRepository, + ) { + } + + public parseBasicAuthFromRequest(req: express.Request): { username: string, password: string } { + const authorization = req.header('authorization'); + + if (authorization && authorization.split(' ')[0] === 'Basic') { + this.log.info('Credentials provided by the client'); + const decodedBase64 = Buffer.from(authorization.split(' ')[1], 'base64').toString('ascii'); + const username = decodedBase64.split(':')[0]; + const password = decodedBase64.split(':')[1]; + if (username && password) { + return { username, password }; + } } - public async validateUser(username: string, password: string): Promise { - const user = await this.userRepository.findOne({ - where: { - username, - }, - }); + this.log.info('No credentials provided by the client'); + return undefined; + } - if (await User.comparePassword(user, password)) { - return user; - } + public async validateUser(username: string, password: string): Promise { + const user = await this.userRepository.findOne({ + where: { + username, + }, + }); - return undefined; + if (await SampleUser.comparePassword(user, password)) { + return user; } + return undefined; + } + } diff --git a/src/auth/authorizationChecker.ts b/src/auth/authorizationChecker.ts index da7cd984..56b26111 100644 --- a/src/auth/authorizationChecker.ts +++ b/src/auth/authorizationChecker.ts @@ -6,29 +6,29 @@ import { Logger } from '../lib/logger'; import { AuthService } from './AuthService'; export function authorizationChecker(connection: Connection): (action: Action, roles: any[]) => Promise | boolean { - const log = new Logger(__filename); - const authService = Container.get(AuthService); + const log = new Logger(__filename); + const authService = Container.get(AuthService); - return async function innerAuthorizationChecker(action: Action, roles: string[]): Promise { - // here you can use request/response objects from action - // also if decorator defines roles it needs to access the action - // you can use them to provide granular access check - // checker must return either boolean (true or false) - // either promise that resolves a boolean value - const credentials = authService.parseBasicAuthFromRequest(action.request); + return async function innerAuthorizationChecker(action: Action, roles: string[]): Promise { + // here you can use request/response objects from action + // also if decorator defines roles it needs to access the action + // you can use them to provide granular access check + // checker must return either boolean (true or false) + // either promise that resolves a boolean value + const credentials = authService.parseBasicAuthFromRequest(action.request); - if (credentials === undefined) { - log.warn('No credentials given'); - return false; - } + if (credentials === undefined) { + log.warn('No credentials given'); + return false; + } - action.request.user = await authService.validateUser(credentials.username, credentials.password); - if (action.request.user === undefined) { - log.warn('Invalid credentials given'); - return false; - } + action.request.user = await authService.validateUser(credentials.username, credentials.password); + if (action.request.user === undefined) { + log.warn('Invalid credentials given'); + return false; + } - log.info('Successfully checked credentials'); - return true; - }; + log.info('Successfully checked credentials'); + return true; + }; } diff --git a/src/auth/currentUserChecker.ts b/src/auth/currentUserChecker.ts index b4979440..1784f423 100644 --- a/src/auth/currentUserChecker.ts +++ b/src/auth/currentUserChecker.ts @@ -1,10 +1,10 @@ import { Action } from 'routing-controllers'; import { Connection } from 'typeorm'; -import { User } from '../api/models/User'; +import { SampleUser } from '../api/models/SampleUser'; -export function currentUserChecker(connection: Connection): (action: Action) => Promise { - return async function innerCurrentUserChecker(action: Action): Promise { - return action.request.user; - }; +export function currentUserChecker(connection: Connection): (action: Action) => Promise { + return async function innerCurrentUserChecker(action: Action): Promise { + return action.request.user; + }; } diff --git a/src/database/factories/PetFactory.ts b/src/database/factories/PetFactory.ts deleted file mode 100644 index 4d15913c..00000000 --- a/src/database/factories/PetFactory.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as Faker from 'faker'; -import { define } from 'typeorm-seeding'; -import * as uuid from 'uuid'; - -import { Pet } from '../../../src/api/models/Pet'; - -define(Pet, (faker: typeof Faker) => { - const gender = faker.random.number(1); - const name = faker.name.firstName(gender); - - const pet = new Pet(); - pet.id = uuid.v1(); - pet.name = name; - pet.age = faker.random.number(); - return pet; -}); diff --git a/src/database/factories/SamplePetFactory.ts b/src/database/factories/SamplePetFactory.ts new file mode 100644 index 00000000..bdd4cbca --- /dev/null +++ b/src/database/factories/SamplePetFactory.ts @@ -0,0 +1,16 @@ +import * as Faker from 'faker'; +import { define } from 'typeorm-seeding'; +import * as uuid from 'uuid'; + +import { SamplePet } from '../../api/models/SamplePet'; + +define(SamplePet, (faker: typeof Faker) => { + const gender = faker.random.number(1); + const name = faker.name.firstName(gender); + + const pet = new SamplePet(); + pet.id = uuid.v1(); + pet.name = name; + pet.age = faker.random.number(); + return pet; +}); diff --git a/src/database/factories/SampleUserFactory.ts b/src/database/factories/SampleUserFactory.ts new file mode 100644 index 00000000..47c8fc62 --- /dev/null +++ b/src/database/factories/SampleUserFactory.ts @@ -0,0 +1,22 @@ +import * as Faker from 'faker'; +import { define } from 'typeorm-seeding'; +import * as uuid from 'uuid'; + +import { SampleUser } from '../../api/models/SampleUser'; + +define(SampleUser, (faker: typeof Faker, settings: { role: string }) => { + const gender = faker.random.number(1); + const firstName = faker.name.firstName(gender); + const lastName = faker.name.lastName(gender); + const email = faker.internet.email(firstName, lastName); + const username = faker.internet.userName(firstName, lastName); + + const user = new SampleUser(); + user.id = uuid.v1(); + user.firstName = firstName; + user.lastName = lastName; + user.email = email; + user.username = username; + user.password = '1234'; + return user; +}); diff --git a/src/database/factories/UserFactory.ts b/src/database/factories/UserFactory.ts deleted file mode 100644 index 4cf4e3ca..00000000 --- a/src/database/factories/UserFactory.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Faker from 'faker'; -import { define } from 'typeorm-seeding'; -import * as uuid from 'uuid'; - -import { User } from '../../../src/api/models/User'; - -define(User, (faker: typeof Faker, settings: { role: string }) => { - const gender = faker.random.number(1); - const firstName = faker.name.firstName(gender); - const lastName = faker.name.lastName(gender); - const email = faker.internet.email(firstName, lastName); - const username = faker.internet.userName(firstName, lastName); - - const user = new User(); - user.id = uuid.v1(); - user.firstName = firstName; - user.lastName = lastName; - user.email = email; - user.username = username; - user.password = '1234'; - return user; -}); diff --git a/src/database/migrations/1511105183653-CreateSampleUserTable.ts b/src/database/migrations/1511105183653-CreateSampleUserTable.ts new file mode 100644 index 00000000..b0cd2935 --- /dev/null +++ b/src/database/migrations/1511105183653-CreateSampleUserTable.ts @@ -0,0 +1,55 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +export class CreateSampleUserTable1511105183653 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + const table = new Table({ + name: 'sample_user', + columns: [ + { + name: 'id', + type: 'varchar', + length: '255', + isPrimary: true, + isNullable: false, + }, { + name: 'first_name', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, { + name: 'last_name', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, { + name: 'email', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, { + name: 'username', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, { + name: 'password', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, + ], + }); + await queryRunner.createTable(table); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('sample_user'); + } + +} diff --git a/src/database/migrations/1511105183653-CreateUserTable.ts b/src/database/migrations/1511105183653-CreateUserTable.ts deleted file mode 100644 index 1f7e61d6..00000000 --- a/src/database/migrations/1511105183653-CreateUserTable.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { MigrationInterface, QueryRunner, Table } from 'typeorm'; - -export class CreateUserTable1511105183653 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const table = new Table({ - name: 'user', - columns: [ - { - name: 'id', - type: 'varchar', - length: '255', - isPrimary: true, - isNullable: false, - }, { - name: 'first_name', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - }, { - name: 'last_name', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - }, { - name: 'email', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - }, { - name: 'username', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - } , { - name: 'password', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - }, - ], - }); - await queryRunner.createTable(table); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('user'); - } - -} diff --git a/src/database/migrations/1512663524808-CreatePetTable.ts b/src/database/migrations/1512663524808-CreatePetTable.ts deleted file mode 100644 index 9298359d..00000000 --- a/src/database/migrations/1512663524808-CreatePetTable.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner, Table } from 'typeorm'; - -export class CreatePetTable1512663524808 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const table = new Table({ - name: 'pet', - columns: [ - { - name: 'id', - type: 'varchar', - length: '255', - isPrimary: true, - isNullable: false, - }, { - name: 'name', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: false, - }, { - name: 'age', - type: 'int', - isPrimary: false, - isNullable: false, - }, { - name: 'user_id', - type: 'varchar', - length: '255', - isPrimary: false, - isNullable: true, - }, - ], - }); - await queryRunner.createTable(table); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('pet'); - } - -} diff --git a/src/database/migrations/1512663524808-CreateSamplePetTable.ts b/src/database/migrations/1512663524808-CreateSamplePetTable.ts new file mode 100644 index 00000000..1e64b01a --- /dev/null +++ b/src/database/migrations/1512663524808-CreateSamplePetTable.ts @@ -0,0 +1,42 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +export class CreateSamplePetTable1512663524808 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + const table = new Table({ + name: 'sample_pet', + columns: [ + { + name: 'id', + type: 'varchar', + length: '255', + isPrimary: true, + isNullable: false, + }, { + name: 'name', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: false, + }, { + name: 'age', + type: 'int', + isPrimary: false, + isNullable: false, + }, { + name: 'user_id', + type: 'varchar', + length: '255', + isPrimary: false, + isNullable: true, + }, + ], + }); + await queryRunner.createTable(table); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('sample_pet'); + } + +} diff --git a/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts index 8f345f12..6c4d65cf 100644 --- a/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts +++ b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts @@ -6,16 +6,16 @@ export class AddUserRelationToPetTable1512663990063 implements MigrationInterfac name: 'fk_user_pet', columnNames: ['user_id'], referencedColumnNames: ['id'], - referencedTableName: 'user', + referencedTableName: 'sample_user', onDelete: 'CASCADE', }); public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createForeignKey('pet', this.tableForeignKey); + await queryRunner.createForeignKey('sample_pet', this.tableForeignKey); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropForeignKey('pet', this.tableForeignKey); + await queryRunner.dropForeignKey('sample_pet', this.tableForeignKey); } } diff --git a/src/database/seeds/CreateBruce.ts b/src/database/seeds/CreateBruce.ts deleted file mode 100644 index f40467a9..00000000 --- a/src/database/seeds/CreateBruce.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Connection } from 'typeorm'; -import { Factory, Seed } from 'typeorm-seeding'; -import * as uuid from 'uuid'; - -import { User } from '../../../src/api/models/User'; - -export class CreateBruce implements Seed { - - public async seed(factory: Factory, connection: Connection): Promise { - // const userFactory = factory(User as any); - // const adminUserFactory = userFactory({ role: 'admin' }); - - // const bruce = await adminUserFactory.make(); - // console.log(bruce); - - // const bruce2 = await adminUserFactory.seed(); - // console.log(bruce2); - - // const bruce3 = await adminUserFactory - // .map(async (e: User) => { - // e.firstName = 'Bruce'; - // return e; - // }) - // .seed(); - // console.log(bruce3); - - // return bruce; - - // const connection = await factory.getConnection(); - const em = connection.createEntityManager(); - - const user = new User(); - user.id = uuid.v1(); - user.firstName = 'Bruce'; - user.lastName = 'Wayne'; - user.email = 'bruce.wayne@wayne-enterprises.com'; - user.username = 'bruce'; - user.password = '1234'; - return await em.save(user); - } - -} diff --git a/src/database/seeds/CreatePets.ts b/src/database/seeds/CreatePets.ts deleted file mode 100644 index a97adebe..00000000 --- a/src/database/seeds/CreatePets.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Connection } from 'typeorm'; -import { Factory, Seed, times } from 'typeorm-seeding'; - -import { Pet } from '../../../src/api/models/Pet'; -import { User } from '../../../src/api/models/User'; - -export class CreatePets implements Seed { - - public async seed(factory: Factory, connection: Connection): Promise { - const em = connection.createEntityManager(); - await times(10, async (n) => { - const pet = await factory(Pet)().seed(); - const user = await factory(User)().make(); - user.pets = [pet]; - return await em.save(user); - }); - } - -} diff --git a/src/database/seeds/CreateSampleBruce.ts b/src/database/seeds/CreateSampleBruce.ts new file mode 100644 index 00000000..665954d4 --- /dev/null +++ b/src/database/seeds/CreateSampleBruce.ts @@ -0,0 +1,42 @@ +import { Connection } from 'typeorm'; +import { Factory, Seed } from 'typeorm-seeding'; +import * as uuid from 'uuid'; + +import { SampleUser } from '../../api/models/SampleUser'; + +export class CreateSampleBruce implements Seed { + + public async seed(factory: Factory, connection: Connection): Promise { + // const userFactory = factory(SampleUser as any); + // const adminUserFactory = userFactory({ role: 'admin' }); + + // const bruce = await adminUserFactory.make(); + // console.log(bruce); + + // const bruce2 = await adminUserFactory.seed(); + // console.log(bruce2); + + // const bruce3 = await adminUserFactory + // .map(async (e: SampleUser) => { + // e.firstName = 'Bruce'; + // return e; + // }) + // .seed(); + // console.log(bruce3); + + // return bruce; + + // const connection = await factory.getConnection(); + const em = connection.createEntityManager(); + + const user = new SampleUser(); + user.id = uuid.v1(); + user.firstName = 'Bruce'; + user.lastName = 'Wayne'; + user.email = 'bruce.wayne@wayne-enterprises.com'; + user.username = 'bruce'; + user.password = '1234'; + return await em.save(user); + } + +} diff --git a/src/database/seeds/CreateSamplePets.ts b/src/database/seeds/CreateSamplePets.ts new file mode 100644 index 00000000..a1c8a786 --- /dev/null +++ b/src/database/seeds/CreateSamplePets.ts @@ -0,0 +1,19 @@ +import { Connection } from 'typeorm'; +import { Factory, Seed, times } from 'typeorm-seeding'; + +import { SamplePet } from '../../api/models/SamplePet'; +import { SampleUser } from '../../api/models/SampleUser'; + +export class CreateSamplePets implements Seed { + + public async seed(factory: Factory, connection: Connection): Promise { + const em = connection.createEntityManager(); + await times(10, async (n) => { + const pet = await factory(SamplePet)().seed(); + const user = await factory(SampleUser)().make(); + user.pets = [pet]; + return await em.save(user); + }); + } + +} diff --git a/src/database/seeds/CreateSampleUsers.ts b/src/database/seeds/CreateSampleUsers.ts new file mode 100644 index 00000000..e07a1942 --- /dev/null +++ b/src/database/seeds/CreateSampleUsers.ts @@ -0,0 +1,12 @@ +import { Factory, Seed } from 'typeorm-seeding'; +import { Connection } from 'typeorm/connection/Connection'; + +import { SampleUser } from '../../api/models/SampleUser'; + +export class CreateSampleUsers implements Seed { + + public async seed(factory: Factory, connection: Connection): Promise { + await factory(SampleUser)().seedMany(10); + } + +} diff --git a/src/database/seeds/CreateUsers.ts b/src/database/seeds/CreateUsers.ts deleted file mode 100644 index 72d7f16d..00000000 --- a/src/database/seeds/CreateUsers.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Factory, Seed } from 'typeorm-seeding'; -import { Connection } from 'typeorm/connection/Connection'; - -import { User } from '../../../src/api/models/User'; - -export class CreateUsers implements Seed { - - public async seed(factory: Factory, connection: Connection): Promise { - await factory(User)().seedMany(10); - } - -} diff --git a/src/decorators/DLoader.ts b/src/decorators/DLoader.ts index 5d051afe..8a14465d 100644 --- a/src/decorators/DLoader.ts +++ b/src/decorators/DLoader.ts @@ -3,11 +3,11 @@ import { Container, ObjectType } from 'typedi'; import { createDataLoader, CreateDataLoaderOptions } from '../lib/graphql'; export function DLoader(obj: ObjectType, options: CreateDataLoaderOptions = {}): ParameterDecorator { - return (object, propertyKey, index) => { - const dataLoader = createDataLoader(obj, options); - const propertyName = propertyKey ? propertyKey.toString() : ''; - Container.registerHandler({ object, propertyName, index, value: () => dataLoader }); - }; + return (object, propertyKey, index) => { + const dataLoader = createDataLoader(obj, options); + const propertyName = propertyKey ? propertyKey.toString() : ''; + Container.registerHandler({ object, propertyName, index, value: () => dataLoader }); + }; } export * from '../lib/graphql'; diff --git a/src/decorators/EventDispatcher.ts b/src/decorators/EventDispatcher.ts index d4741f33..c4720b3e 100644 --- a/src/decorators/EventDispatcher.ts +++ b/src/decorators/EventDispatcher.ts @@ -2,10 +2,10 @@ import { EventDispatcher as EventDispatcherClass } from 'event-dispatch'; import { Container } from 'typedi'; export function EventDispatcher(): any { - return (object: any, propertyName: string, index?: number): any => { - const eventDispatcher = new EventDispatcherClass(); - Container.registerHandler({ object, propertyName, index, value: () => eventDispatcher }); - }; + return (object: any, propertyName: string, index?: number): any => { + const eventDispatcher = new EventDispatcherClass(); + Container.registerHandler({ object, propertyName, index, value: () => eventDispatcher }); + }; } export { EventDispatcher as EventDispatcherInterface } from 'event-dispatch'; diff --git a/src/decorators/Logger.ts b/src/decorators/Logger.ts index ad03c626..4c0c7a04 100644 --- a/src/decorators/Logger.ts +++ b/src/decorators/Logger.ts @@ -3,11 +3,11 @@ import { Container } from 'typedi'; import { Logger as WinstonLogger } from '../lib/logger'; export function Logger(scope: string): ParameterDecorator { - return (object, propertyKey, index): any => { - const logger = new WinstonLogger(scope); - const propertyName = propertyKey ? propertyKey.toString() : ''; - Container.registerHandler({ object, propertyName, index, value: () => logger }); - }; + return (object, propertyKey, index): any => { + const logger = new WinstonLogger(scope); + const propertyName = propertyKey ? propertyKey.toString() : ''; + Container.registerHandler({ object, propertyName, index, value: () => logger }); + }; } export { LoggerInterface } from '../lib/logger'; diff --git a/src/env.ts b/src/env.ts index c14cff00..0795bc31 100644 --- a/src/env.ts +++ b/src/env.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as pkg from '../package.json'; import { - getOsEnv, getOsEnvOptional, getOsPath, getOsPaths, normalizePort, toBool, toNumber + getOsEnv, getOsEnvOptional, getOsPath, getOsPaths, normalizePort, toBool, toNumber, } from './lib/env'; /** @@ -15,61 +15,67 @@ dotenv.config({ path: path.join(process.cwd(), `.env${((process.env.NODE_ENV === * Environment variables */ export const env = { - node: process.env.NODE_ENV || 'development', - isProduction: process.env.NODE_ENV === 'production', - isTest: process.env.NODE_ENV === 'test', - isDevelopment: process.env.NODE_ENV === 'development', - app: { - name: getOsEnv('APP_NAME'), - version: (pkg as any).version, - description: (pkg as any).description, - host: getOsEnv('APP_HOST'), - schema: getOsEnv('APP_SCHEMA'), - routePrefix: getOsEnv('APP_ROUTE_PREFIX'), - port: normalizePort(process.env.PORT || getOsEnv('APP_PORT')), - banner: toBool(getOsEnv('APP_BANNER')), - dirs: { - migrations: getOsPaths('TYPEORM_MIGRATIONS'), - migrationsDir: getOsPath('TYPEORM_MIGRATIONS_DIR'), - entities: getOsPaths('TYPEORM_ENTITIES'), - entitiesDir: getOsPath('TYPEORM_ENTITIES_DIR'), - controllers: getOsPaths('CONTROLLERS'), - middlewares: getOsPaths('MIDDLEWARES'), - interceptors: getOsPaths('INTERCEPTORS'), - subscribers: getOsPaths('SUBSCRIBERS'), - resolvers: getOsPaths('RESOLVERS'), - }, - }, - log: { - level: getOsEnv('LOG_LEVEL'), - json: toBool(getOsEnvOptional('LOG_JSON')), - output: getOsEnv('LOG_OUTPUT'), - }, - db: { - type: getOsEnv('TYPEORM_CONNECTION'), - host: getOsEnvOptional('TYPEORM_HOST'), - port: toNumber(getOsEnvOptional('TYPEORM_PORT')), - username: getOsEnvOptional('TYPEORM_USERNAME'), - password: getOsEnvOptional('TYPEORM_PASSWORD'), - database: getOsEnv('TYPEORM_DATABASE'), - synchronize: toBool(getOsEnvOptional('TYPEORM_SYNCHRONIZE')), - logging: getOsEnv('TYPEORM_LOGGING'), - }, - graphql: { - enabled: toBool(getOsEnv('GRAPHQL_ENABLED')), - route: getOsEnv('GRAPHQL_ROUTE'), - editor: toBool(getOsEnv('GRAPHQL_EDITOR')), - }, - swagger: { - enabled: toBool(getOsEnv('SWAGGER_ENABLED')), - route: getOsEnv('SWAGGER_ROUTE'), - username: getOsEnv('SWAGGER_USERNAME'), - password: getOsEnv('SWAGGER_PASSWORD'), - }, - monitor: { - enabled: toBool(getOsEnv('MONITOR_ENABLED')), - route: getOsEnv('MONITOR_ROUTE'), - username: getOsEnv('MONITOR_USERNAME'), - password: getOsEnv('MONITOR_PASSWORD'), + node: process.env.NODE_ENV || 'development', + isProduction: process.env.NODE_ENV === 'production', + isTest: process.env.NODE_ENV === 'test', + isDevelopment: process.env.NODE_ENV === 'development', + app: { + name: getOsEnv('APP_NAME'), + version: (pkg as any).version, + description: (pkg as any).description, + host: getOsEnv('APP_HOST'), + schema: getOsEnv('APP_SCHEMA'), + routePrefix: getOsEnv('APP_ROUTE_PREFIX'), + port: normalizePort(process.env.PORT || getOsEnv('APP_PORT')), + banner: toBool(getOsEnv('APP_BANNER')), + dirs: { + migrations: getOsPaths('TYPEORM_MIGRATIONS'), + migrationsDir: getOsPath('TYPEORM_MIGRATIONS_DIR'), + entities: getOsPaths('TYPEORM_ENTITIES'), + entitiesDir: getOsPath('TYPEORM_ENTITIES_DIR'), + controllers: getOsPaths('CONTROLLERS'), + middlewares: getOsPaths('MIDDLEWARES'), + interceptors: getOsPaths('INTERCEPTORS'), + subscribers: getOsPaths('SUBSCRIBERS'), + resolvers: getOsPaths('RESOLVERS'), }, + }, + aws: { + accessKeyId: getOsEnv('AWS_ACCESS_KEY_ID'), + secretAccessKey: getOsEnv('AWS_SECRET_ACCESS_KEY'), + region: getOsEnv('AWS_REGION'), + }, + log: { + level: getOsEnv('LOG_LEVEL'), + json: toBool(getOsEnvOptional('LOG_JSON')), + output: getOsEnv('LOG_OUTPUT'), + }, + db: { + enabled: toBool(getOsEnv('TYPEORM_ENABLED')), + type: getOsEnv('TYPEORM_CONNECTION'), + host: getOsEnvOptional('TYPEORM_HOST'), + port: toNumber(getOsEnvOptional('TYPEORM_PORT')), + username: getOsEnvOptional('TYPEORM_USERNAME'), + password: getOsEnvOptional('TYPEORM_PASSWORD'), + database: getOsEnv('TYPEORM_DATABASE'), + synchronize: toBool(getOsEnvOptional('TYPEORM_SYNCHRONIZE')), + logging: getOsEnv('TYPEORM_LOGGING'), + }, + graphql: { + enabled: toBool(getOsEnv('GRAPHQL_ENABLED')), + route: getOsEnv('GRAPHQL_ROUTE'), + editor: toBool(getOsEnv('GRAPHQL_EDITOR')), + }, + swagger: { + enabled: toBool(getOsEnv('SWAGGER_ENABLED')), + route: getOsEnv('SWAGGER_ROUTE'), + username: getOsEnv('SWAGGER_USERNAME'), + password: getOsEnv('SWAGGER_PASSWORD'), + }, + monitor: { + enabled: toBool(getOsEnv('MONITOR_ENABLED')), + route: getOsEnv('MONITOR_ROUTE'), + username: getOsEnv('MONITOR_USERNAME'), + password: getOsEnv('MONITOR_PASSWORD'), + }, }; diff --git a/src/lib/banner.ts b/src/lib/banner.ts index 4cc1da5f..2a93bab5 100644 --- a/src/lib/banner.ts +++ b/src/lib/banner.ts @@ -1,30 +1,30 @@ import { env } from '../env'; -import { Logger } from '../lib/logger'; +import { Logger } from './logger'; export function banner(log: Logger): void { - if (env.app.banner) { - const route = () => `${env.app.schema}://${env.app.host}:${env.app.port}`; - log.info(``); - log.info(`Aloha, your app is ready on ${route()}${env.app.routePrefix}`); - log.info(`To shut it down, press + C at any time.`); - log.info(``); - log.info('-------------------------------------------------------'); - log.info(`Environment : ${env.node}`); - log.info(`Version : ${env.app.version}`); - log.info(``); - log.info(`API Info : ${route()}${env.app.routePrefix}`); - if (env.graphql.enabled) { - log.info(`GraphQL : ${route()}${env.graphql.route}`); - } - if (env.swagger.enabled) { - log.info(`Swagger : ${route()}${env.swagger.route}`); - } - if (env.monitor.enabled) { - log.info(`Monitor : ${route()}${env.monitor.route}`); - } - log.info('-------------------------------------------------------'); - log.info(''); - } else { - log.info(`Application is up and running.`); + if (env.app.banner) { + const route = () => `${env.app.schema}://${env.app.host}:${env.app.port}`; + log.info(``); + log.info(`Aloha, your app is ready on ${route()}${env.app.routePrefix}`); + log.info(`To shut it down, press + C at any time.`); + log.info(``); + log.info('-------------------------------------------------------'); + log.info(`Environment : ${env.node}`); + log.info(`Version : ${env.app.version}`); + log.info(``); + log.info(`API Info : ${route()}${env.app.routePrefix}`); + if (env.graphql.enabled) { + log.info(`GraphQL : ${route()}${env.graphql.route}`); } + if (env.swagger.enabled) { + log.info(`Swagger : ${route()}${env.swagger.route}`); + } + if (env.monitor.enabled) { + log.info(`Monitor : ${route()}${env.monitor.route}`); + } + log.info('-------------------------------------------------------'); + log.info(''); + } else { + log.info(`Application is up and running.`); + } } diff --git a/src/lib/env/utils.ts b/src/lib/env/utils.ts index ee555f69..a466eae7 100644 --- a/src/lib/env/utils.ts +++ b/src/lib/env/utils.ts @@ -1,54 +1,54 @@ import { join } from 'path'; export function getOsEnv(key: string): string { - if (typeof process.env[key] === 'undefined') { - throw new Error(`Environment variable ${key} is not set.`); - } + if (typeof process.env[key] === 'undefined') { + throw new Error(`Environment variable ${key} is not set.`); + } - return process.env[key] as string; + return process.env[key] as string; } export function getOsEnvOptional(key: string): string | undefined { - return process.env[key]; + return process.env[key]; } export function getPath(path: string): string { - return (process.env.NODE_ENV === 'production') - ? join(process.cwd(), path.replace('src/', 'dist/').slice(0, -3) + '.js') - : join(process.cwd(), path); + return (process.env.NODE_ENV === 'production') + ? join(process.cwd(), path.replace('src/', 'dist/').slice(0, -3) + '.js') + : join(process.cwd(), path); } export function getPaths(paths: string[]): string[] { - return paths.map(p => getPath(p)); + return paths.map(p => getPath(p)); } export function getOsPath(key: string): string { - return getPath(getOsEnv(key)); + return getPath(getOsEnv(key)); } export function getOsPaths(key: string): string[] { - return getPaths(getOsEnvArray(key)); + return getPaths(getOsEnvArray(key)); } export function getOsEnvArray(key: string, delimiter: string = ','): string[] { - return process.env[key] && process.env[key].split(delimiter) || []; + return process.env[key] && process.env[key].split(delimiter) || []; } export function toNumber(value: string): number { - return parseInt(value, 10); + return parseInt(value, 10); } export function toBool(value: string): boolean { - return value === 'true'; + return value === 'true'; } export function normalizePort(port: string): number | string | boolean { - const parsedPort = parseInt(port, 10); - if (isNaN(parsedPort)) { // named pipe - return port; - } - if (parsedPort >= 0) { // port number - return parsedPort; - } - return false; + const parsedPort = parseInt(port, 10); + if (isNaN(parsedPort)) { // named pipe + return port; + } + if (parsedPort >= 0) { // port number + return parsedPort; + } + return false; } diff --git a/src/lib/graphql/graphql-error-handling.ts b/src/lib/graphql/graphql-error-handling.ts index 560bda9a..806314f5 100644 --- a/src/lib/graphql/graphql-error-handling.ts +++ b/src/lib/graphql/graphql-error-handling.ts @@ -2,7 +2,7 @@ import { GraphQLObjectType, GraphQLSchema } from 'graphql'; import * as uuid from 'uuid'; import { env } from '../../env'; -import { Logger } from '../../lib/logger'; +import { Logger } from '../logger'; // This feature is a copy from https://github.com/kadirahq/graphql-errors const logger = new Logger('app:errors'); @@ -15,111 +15,111 @@ export const IsUserError = Symbol(); // UserErrors will be sent to the user export class UserError extends Error { - constructor(...args: any[]) { - super(args[0]); - this.name = 'Error'; - this.message = args[0]; - this[IsUserError] = true; - Error.captureStackTrace(this); - } + constructor(...args: any[]) { + super(args[0]); + this.name = 'Error'; + this.message = args[0]; + this[IsUserError] = true; + Error.captureStackTrace(this); + } } // Modifies errors before sending to the user export let defaultHandler = (err?) => { - if (err[IsUserError]) { - return err; - } - const errId = uuid.v4(); - err.message = `${err.message}: ${errId}`; - if (!env.isTest) { - console.error(err && err.stack || err); - } - if (env.isProduction) { - logger.error(err); - } - err.message = `500: Internal Error: ${errId}`; + if (err[IsUserError]) { return err; + } + const errId = uuid.v4(); + err.message = `${err.message}: ${errId}`; + if (!env.isTest) { + console.error(err && err.stack || err); + } + if (env.isProduction) { + logger.error(err); + } + err.message = `500: Internal Error: ${errId}`; + return err; }; const maskField = (field, fn) => { - const resolveFn = field.resolve; - if (field[Processed] || !resolveFn) { - return; + const resolveFn = field.resolve; + if (field[Processed] || !resolveFn) { + return; + } + + field[Processed] = true; + field.resolve = async (...args) => { + try { + const out = resolveFn.call(undefined, ...args); + return await Promise.resolve(out); + } catch (e) { + throw fn(e); } + }; - field[Processed] = true; - field.resolve = async (...args) => { - try { - const out = resolveFn.call(undefined, ...args); - return await Promise.resolve(out); - } catch (e) { - throw fn(e); - } - }; - - // save the original resolve function - field.resolve._resolveFn = resolveFn; + // save the original resolve function + field.resolve._resolveFn = resolveFn; }; const maskType = (type, fn) => { - if (type[Processed] || !type.getFields) { - return; - } - - const fields = type.getFields(); - for (const fieldName in fields) { - if (!Object.hasOwnProperty.call(fields, fieldName)) { - continue; - } - maskField(fields[fieldName], fn); + if (type[Processed] || !type.getFields) { + return; + } + + const fields = type.getFields(); + for (const fieldName in fields) { + if (!Object.hasOwnProperty.call(fields, fieldName)) { + continue; } + maskField(fields[fieldName], fn); + } }; const maskSchema = (schema, fn) => { - const types = schema.getTypeMap(); - for (const typeName in types) { - if (!Object.hasOwnProperty.call(types, typeName)) { - continue; - } - maskType(types[typeName], fn); + const types = schema.getTypeMap(); + for (const typeName in types) { + if (!Object.hasOwnProperty.call(types, typeName)) { + continue; } + maskType(types[typeName], fn); + } }; // Changes the default error handler function export const setDefaultHandler = (handlerFn) => { - defaultHandler = handlerFn; + defaultHandler = handlerFn; }; // Masks graphql schemas, types or individual fields export const handlingErrors = (thing, fn = defaultHandler) => { - if (thing instanceof GraphQLSchema) { - maskSchema(thing, fn); - } else if (thing instanceof GraphQLObjectType) { - maskType(thing, fn); - } else { - maskField(thing, fn); - } + if (thing instanceof GraphQLSchema) { + maskSchema(thing, fn); + } else if (thing instanceof GraphQLObjectType) { + maskType(thing, fn); + } else { + maskField(thing, fn); + } }; export const getErrorCode = (message: string): string => { - if (hasErrorCode(message)) { - return message.substring(0, 3); - } - return '500'; // unkown error code + if (hasErrorCode(message)) { + return message.substring(0, 3); + } + return '500'; // unkown error code }; export const getErrorMessage = (message: string): string => { - if (hasErrorCode(message)) { - return message.substring(5); - } - return message; + if (hasErrorCode(message)) { + return message.substring(5); + } + return message; }; export const hasErrorCode = (error: any): boolean => { - let message = error; - if (error.message) { - message = error.message; - } - const reg = new RegExp('^[0-9]{3}: '); - return reg.test(message); + let message = error; + if (error.message) { + message = error.message; + } + const reg = new RegExp('^[0-9]{3}: '); + return reg.test(message); }; diff --git a/src/lib/graphql/index.ts b/src/lib/graphql/index.ts index 2bf184fd..488a3f7c 100644 --- a/src/lib/graphql/index.ts +++ b/src/lib/graphql/index.ts @@ -13,35 +13,35 @@ export * from './graphql-error-handling'; // ------------------------------------------------------------------------- export interface CreateDataLoaderOptions { - method?: string; - key?: string; - multiple?: boolean; + method?: string; + key?: string; + multiple?: boolean; } /** * Creates a new dataloader with the typorm repository */ export function createDataLoader(obj: ObjectType, options: CreateDataLoaderOptions = {}): DataLoader { - let repository; + let repository; + try { + repository = getCustomRepository>(obj); + } catch (errorRepo) { try { - repository = getCustomRepository>(obj); - } catch (errorRepo) { - try { - repository = getRepository(obj); - } catch (errorModel) { - throw new Error('Could not create a dataloader, because obj is nether model or repository!'); - } + repository = getRepository(obj); + } catch (errorModel) { + throw new Error('Could not create a dataloader, because obj is nether model or repository!'); } + } - return new DataLoader(async (ids: number[]) => { - let items = []; - if (options.method) { - items = await repository[options.method](ids); - } else { - items = await repository.findByIds(ids); - } + return new DataLoader(async (ids: number[]) => { + let items = []; + if (options.method) { + items = await repository[options.method](ids); + } else { + items = await repository.findByIds(ids); + } - const handleBatch = (arr: any[]) => options.multiple === true ? arr : arr[0]; - return ids.map(id => handleBatch(items.filter(item => item[options.key || 'id'] === id))); - }); + const handleBatch = (arr: any[]) => options.multiple === true ? arr : arr[0]; + return ids.map(id => handleBatch(items.filter(item => item[options.key || 'id'] === id))); + }); } diff --git a/src/lib/logger/Logger.ts b/src/lib/logger/Logger.ts index ba26f213..56550293 100644 --- a/src/lib/logger/Logger.ts +++ b/src/lib/logger/Logger.ts @@ -14,50 +14,50 @@ import * as winston from 'winston'; export class Logger { - public static DEFAULT_SCOPE = 'app'; - - private static parsePathToScope(filepath: string): string { - if (filepath.indexOf(path.sep) >= 0) { - filepath = filepath.replace(process.cwd(), ''); - filepath = filepath.replace(`${path.sep}src${path.sep}`, ''); - filepath = filepath.replace(`${path.sep}dist${path.sep}`, ''); - filepath = filepath.replace('.ts', ''); - filepath = filepath.replace('.js', ''); - filepath = filepath.replace(path.sep, ':'); - } - return filepath; + public static DEFAULT_SCOPE = 'app'; + + private static parsePathToScope(filepath: string): string { + if (filepath.indexOf(path.sep) >= 0) { + filepath = filepath.replace(process.cwd(), ''); + filepath = filepath.replace(`${path.sep}src${path.sep}`, ''); + filepath = filepath.replace(`${path.sep}dist${path.sep}`, ''); + filepath = filepath.replace('.ts', ''); + filepath = filepath.replace('.js', ''); + filepath = filepath.replace(path.sep, ':'); } + return filepath; + } - private scope: string; + private scope: string; - constructor(scope?: string) { - this.scope = Logger.parsePathToScope((scope) ? scope : Logger.DEFAULT_SCOPE); - } + constructor(scope?: string) { + this.scope = Logger.parsePathToScope((scope) ? scope : Logger.DEFAULT_SCOPE); + } - public debug(message: string, ...args: any[]): void { - this.log('debug', message, args); - } + public debug(message: string, ...args: any[]): void { + this.log('debug', message, args); + } - public info(message: string, ...args: any[]): void { - this.log('info', message, args); - } + public info(message: string, ...args: any[]): void { + this.log('info', message, args); + } - public warn(message: string, ...args: any[]): void { - this.log('warn', message, args); - } + public warn(message: string, ...args: any[]): void { + this.log('warn', message, args); + } - public error(message: string, ...args: any[]): void { - this.log('error', message, args); - } + public error(message: string, ...args: any[]): void { + this.log('error', message, args); + } - private log(level: string, message: string, args: any[]): void { - if (winston) { - winston[level](`${this.formatScope()} ${message}`, args); - } + private log(level: string, message: string, args: any[]): void { + if (winston) { + winston[level](`${this.formatScope()} ${message}`, args); } + } - private formatScope(): string { - return `[${this.scope}]`; - } + private formatScope(): string { + return `[${this.scope}]`; + } } diff --git a/src/lib/logger/LoggerInterface.ts b/src/lib/logger/LoggerInterface.ts index 75a635c3..f271c0cb 100644 --- a/src/lib/logger/LoggerInterface.ts +++ b/src/lib/logger/LoggerInterface.ts @@ -1,6 +1,9 @@ export interface LoggerInterface { - debug(message: string, ...args: any[]): void; - info(message: string, ...args: any[]): void; - warn(message: string, ...args: any[]): void; - error(message: string, ...args: any[]): void; + debug(message: string, ...args: any[]): void; + + info(message: string, ...args: any[]): void; + + warn(message: string, ...args: any[]): void; + + error(message: string, ...args: any[]): void; } diff --git a/src/loaders/eventDispatchLoader.ts b/src/loaders/eventDispatchLoader.ts index 588290f5..1b388927 100644 --- a/src/loaders/eventDispatchLoader.ts +++ b/src/loaders/eventDispatchLoader.ts @@ -10,14 +10,14 @@ import { env } from '../env'; * import them manually */ export const eventDispatchLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings) { - const patterns = env.app.dirs.subscribers; - patterns.forEach((pattern) => { - glob(pattern, (err: any, files: string[]) => { - for (const file of files) { - require(file); - } - }); - }); - } + if (settings) { + const patterns = env.app.dirs.subscribers; + patterns.forEach((pattern) => { + glob(pattern, (err: any, files: string[]) => { + for (const file of files) { + require(file); + } + }); + }); + } }; diff --git a/src/loaders/expressLoader.ts b/src/loaders/expressLoader.ts index f509794f..3907c3e5 100644 --- a/src/loaders/expressLoader.ts +++ b/src/loaders/expressLoader.ts @@ -5,42 +5,62 @@ import { createExpressServer } from 'routing-controllers'; import { authorizationChecker } from '../auth/authorizationChecker'; import { currentUserChecker } from '../auth/currentUserChecker'; import { env } from '../env'; +import { RoutingControllersOptions } from 'routing-controllers/RoutingControllersOptions'; export const expressLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings) { - const connection = settings.getData('connection'); - - /** - * We create a new express server instance. - * We could have also use useExpressServer here to attach controllers to an existing express instance. - */ - const expressApp: Application = createExpressServer({ - cors: true, - classTransformer: true, - routePrefix: env.app.routePrefix, - defaultErrorHandler: false, - /** - * We can add options about how routing-controllers should configure itself. - * Here we specify what controllers should be registered in our express server. - */ - controllers: env.app.dirs.controllers, - middlewares: env.app.dirs.middlewares, - interceptors: env.app.dirs.interceptors, - - /** - * Authorization features - */ - authorizationChecker: authorizationChecker(connection), - currentUserChecker: currentUserChecker(connection), - }); - - // Run application to listen on given port - if (!env.isTest) { - const server = expressApp.listen(env.app.port); - settings.setData('express_server', server); - } - - // Here we can set the data for other loaders - settings.setData('express_app', expressApp); + if (settings) { + const expressOptions: RoutingControllersOptions = { + cors: true, + classTransformer: true, + routePrefix: env.app.routePrefix, + defaultErrorHandler: false, + /** + * We can add options about how routing-controllers should configure itself. + * Here we specify what controllers should be registered in our express server. + */ + controllers: env.app.dirs.controllers, + middlewares: env.app.dirs.middlewares, + interceptors: env.app.dirs.interceptors, + + /** + * class-validator를 활용한 Request Payload 타입 검증 + */ + validation: true, + + // TODO: DB를 사용 안하게 하더라도 Authorization 기능은 쓸 수 있게 개선 + // /** + // * Authorization features + // */ + // authorizationChecker: authorizationChecker(), + // currentUserChecker: currentUserChecker(), + }; + + // DB 사용할 경우 + if (env.db.enabled) { + /** + * TypeOrm connection + */ + const connection = settings.getData('connection'); + /** + * Authorization features + */ + expressOptions.authorizationChecker = authorizationChecker(connection); + expressOptions.currentUserChecker = currentUserChecker(connection); } + + /** + * We create a new express server instance. + * We could have also use useExpressServer here to attach controllers to an existing express instance. + */ + const expressApp: Application = createExpressServer(expressOptions); + + // Run application to listen on given port + if (!env.isTest) { + const server = expressApp.listen(env.app.port); + settings.setData('express_server', server); + } + + // Here we can set the data for other loaders + settings.setData('express_app', expressApp); + } }; diff --git a/src/loaders/graphqlLoader.ts b/src/loaders/graphqlLoader.ts index eb5f8e37..007980cc 100644 --- a/src/loaders/graphqlLoader.ts +++ b/src/loaders/graphqlLoader.ts @@ -9,38 +9,38 @@ import { env } from '../env'; import { getErrorCode, getErrorMessage, handlingErrors } from '../lib/graphql'; export const graphqlLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => { - if (settings && env.graphql.enabled) { - const expressApp = settings.getData('express_app'); - - const schema = await buildSchema({ - resolvers: env.app.dirs.resolvers, - // automatically create `schema.gql` file with schema definition in current folder - emitSchemaFile: path.resolve(__dirname, '../api', 'schema.gql'), - }); - - handlingErrors(schema); - - // Add graphql layer to the express app - expressApp.use(env.graphql.route, (request: express.Request, response: express.Response) => { - - // Build GraphQLContext - const requestId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); // uuid-like - const container = Container.of(requestId); // get scoped container - const context = { requestId, container, request, response }; // create our context - container.set('context', context); // place context or other data in container - - // Setup GraphQL Server - GraphQLHTTP({ - schema, - context, - graphiql: env.graphql.editor, - formatError: error => ({ - code: getErrorCode(error.message), - message: getErrorMessage(error.message), - path: error.path, - }), - })(request, response); - }); - - } + if (settings && env.graphql.enabled) { + const expressApp = settings.getData('express_app'); + + const schema = await buildSchema({ + resolvers: env.app.dirs.resolvers, + // automatically create `schema.gql` file with schema definition in current folder + emitSchemaFile: path.resolve(__dirname, '../api', 'schema.gql'), + }); + + handlingErrors(schema); + + // Add graphql layer to the express app + expressApp.use(env.graphql.route, (request: express.Request, response: express.Response) => { + + // Build GraphQLContext + const requestId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); // uuid-like + const container = Container.of(requestId); // get scoped container + const context = { requestId, container, request, response }; // create our context + container.set('context', context); // place context or other data in container + + // Setup GraphQL Server + GraphQLHTTP({ + schema, + context, + graphiql: env.graphql.editor, + formatError: error => ({ + code: getErrorCode(error.message), + message: getErrorMessage(error.message), + path: error.path, + }), + })(request, response); + }); + + } }; diff --git a/src/loaders/homeLoader.ts b/src/loaders/homeLoader.ts index 8e71e7e5..f79d651c 100644 --- a/src/loaders/homeLoader.ts +++ b/src/loaders/homeLoader.ts @@ -4,18 +4,18 @@ import { MicroframeworkLoader, MicroframeworkSettings } from 'microframework-w3t import { env } from '../env'; export const homeLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings) { - const expressApp = settings.getData('express_app'); - expressApp.get( - env.app.routePrefix, - (req: express.Request, res: express.Response) => { - return res.json({ - name: env.app.name, - version: env.app.version, - description: env.app.description, - }); - } - ); + if (settings) { + const expressApp = settings.getData('express_app'); + expressApp.get( + env.app.routePrefix, + (req: express.Request, res: express.Response) => { + return res.json({ + name: env.app.name, + version: env.app.version, + description: env.app.description, + }); + }, + ); - } + } }; diff --git a/src/loaders/iocLoader.ts b/src/loaders/iocLoader.ts index e4e3fa6a..02d32d8b 100644 --- a/src/loaders/iocLoader.ts +++ b/src/loaders/iocLoader.ts @@ -7,11 +7,11 @@ import { useContainer as ormUseContainer } from 'typeorm'; export const iocLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - /** - * Setup routing-controllers to use typedi container. - */ - routingUseContainer(Container); - ormUseContainer(Container); - classValidatorUseContainer(Container); - typeGraphQLUseContainer(Container); + /** + * Setup routing-controllers to use typedi container. + */ + routingUseContainer(Container); + ormUseContainer(Container); + classValidatorUseContainer(Container); + typeGraphQLUseContainer(Container); }; diff --git a/src/loaders/monitorLoader.ts b/src/loaders/monitorLoader.ts index edab7cb2..699f54a1 100644 --- a/src/loaders/monitorLoader.ts +++ b/src/loaders/monitorLoader.ts @@ -5,20 +5,20 @@ import { MicroframeworkLoader, MicroframeworkSettings } from 'microframework-w3t import { env } from '../env'; export const monitorLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings && env.monitor.enabled) { - const expressApp = settings.getData('express_app'); + if (settings && env.monitor.enabled) { + const expressApp = settings.getData('express_app'); - expressApp.use(monitor()); - expressApp.get( - env.monitor.route, - env.monitor.username ? basicAuth({ - users: { - [`${env.monitor.username}`]: env.monitor.password, - }, - challenge: true, - }) : (req, res, next) => next(), - monitor().pageRoute - ); + expressApp.use(monitor()); + expressApp.get( + env.monitor.route, + env.monitor.username ? basicAuth({ + users: { + [`${env.monitor.username}`]: env.monitor.password, + }, + challenge: true, + }) : (req, res, next) => next(), + monitor().pageRoute, + ); - } + } }; diff --git a/src/loaders/publicLoader.ts b/src/loaders/publicLoader.ts index de08c94c..ad0c7640 100644 --- a/src/loaders/publicLoader.ts +++ b/src/loaders/publicLoader.ts @@ -4,14 +4,14 @@ import * as path from 'path'; import favicon from 'serve-favicon'; export const publicLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings) { - const expressApp = settings.getData('express_app'); - expressApp - // Serve static files like images from the public folder - .use(express.static(path.join(__dirname, '..', 'public'), { maxAge: 31557600000 })) + if (settings) { + const expressApp = settings.getData('express_app'); + expressApp + // Serve static files like images from the public folder + .use(express.static(path.join(__dirname, '..', 'public'), { maxAge: 31557600000 })) - // A favicon is a visual cue that client software, like browsers, use to identify a site - .use(favicon(path.join(__dirname, '..', 'public', 'favicon.ico'))); + // A favicon is a visual cue that client software, like browsers, use to identify a site + .use(favicon(path.join(__dirname, '..', 'public', 'favicon.ico'))); - } + } }; diff --git a/src/loaders/swaggerLoader.ts b/src/loaders/swaggerLoader.ts index 6181981a..7da21053 100644 --- a/src/loaders/swaggerLoader.ts +++ b/src/loaders/swaggerLoader.ts @@ -10,58 +10,58 @@ import * as swaggerUi from 'swagger-ui-express'; import { env } from '../env'; export const swaggerLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - if (settings && env.swagger.enabled) { - const expressApp = settings.getData('express_app'); + if (settings && env.swagger.enabled) { + const expressApp = settings.getData('express_app'); - const { validationMetadatas } = getFromContainer( - MetadataStorage - ) as any; + const { validationMetadatas } = getFromContainer( + MetadataStorage, + ) as any; - const schemas = validationMetadatasToSchemas(validationMetadatas, { - classTransformerMetadataStorage, - refPointerPrefix: '#/components/schemas/', - }); + const schemas = validationMetadatasToSchemas(validationMetadatas, { + classTransformerMetadataStorage, + refPointerPrefix: '#/components/schemas/', + }); - const swaggerFile = routingControllersToSpec( - getMetadataArgsStorage(), - {}, - { - components: { - schemas, - securitySchemes: { - basicAuth: { - type: 'http', - scheme: 'basic', - }, - }, - }, - } - ); + const swaggerFile = routingControllersToSpec( + getMetadataArgsStorage(), + {}, + { + components: { + schemas, + securitySchemes: { + basicAuth: { + type: 'http', + scheme: 'basic', + }, + }, + }, + }, + ); - // Add npm infos to the swagger doc - swaggerFile.info = { - title: env.app.name, - description: env.app.description, - version: env.app.version, - }; + // Add npm infos to the swagger doc + swaggerFile.info = { + title: env.app.name, + description: env.app.description, + version: env.app.version, + }; - swaggerFile.servers = [ - { - url: `${env.app.schema}://${env.app.host}:${env.app.port}${env.app.routePrefix}`, - }, - ]; + swaggerFile.servers = [ + { + url: `${env.app.schema}://${env.app.host}:${env.app.port}${env.app.routePrefix}`, + }, + ]; - expressApp.use( - env.swagger.route, - env.swagger.username ? basicAuth({ - users: { - [`${env.swagger.username}`]: env.swagger.password, - }, - challenge: true, - }) : (req, res, next) => next(), - swaggerUi.serve, - swaggerUi.setup(swaggerFile) - ); + expressApp.use( + env.swagger.route, + env.swagger.username ? basicAuth({ + users: { + [`${env.swagger.username}`]: env.swagger.password, + }, + challenge: true, + }) : (req, res, next) => next(), + swaggerUi.serve, + swaggerUi.setup(swaggerFile), + ); - } + } }; diff --git a/src/loaders/typeormLoader.ts b/src/loaders/typeormLoader.ts index 6c7de288..67976f56 100644 --- a/src/loaders/typeormLoader.ts +++ b/src/loaders/typeormLoader.ts @@ -4,26 +4,27 @@ import { createConnection, getConnectionOptions } from 'typeorm'; import { env } from '../env'; export const typeormLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => { - + if (env.db.enabled) { const loadedConnectionOptions = await getConnectionOptions(); const connectionOptions = Object.assign(loadedConnectionOptions, { - type: env.db.type as any, // See createConnection options for valid types - host: env.db.host, - port: env.db.port, - username: env.db.username, - password: env.db.password, - database: env.db.database, - synchronize: env.db.synchronize, - logging: env.db.logging, - entities: env.app.dirs.entities, - migrations: env.app.dirs.migrations, + type: env.db.type as any, // See createConnection options for valid types + host: env.db.host, + port: env.db.port, + username: env.db.username, + password: env.db.password, + database: env.db.database, + synchronize: env.db.synchronize, + logging: env.db.logging, + entities: env.app.dirs.entities, + migrations: env.app.dirs.migrations, }); const connection = await createConnection(connectionOptions); if (settings) { - settings.setData('connection', connection); - settings.onShutdown(() => connection.close()); + settings.setData('connection', connection); + settings.onShutdown(() => connection.close()); } + } }; diff --git a/src/loaders/winstonLoader.ts b/src/loaders/winstonLoader.ts index a38c5439..91a574fb 100644 --- a/src/loaders/winstonLoader.ts +++ b/src/loaders/winstonLoader.ts @@ -4,20 +4,20 @@ import { configure, format, transports } from 'winston'; import { env } from '../env'; export const winstonLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => { - configure({ - transports: [ - new transports.Console({ - level: env.log.level, - handleExceptions: true, - format: env.node !== 'development' - ? format.combine( - format.json() - ) - : format.combine( - format.colorize(), - format.simple() - ), - }), - ], - }); + configure({ + transports: [ + new transports.Console({ + level: env.log.level, + handleExceptions: true, + format: env.node !== 'development' + ? format.combine( + format.json(), + ) + : format.combine( + format.colorize(), + format.simple(), + ), + }), + ], + }); }; diff --git a/src/types/json.d.ts b/src/types/json.d.ts index 783b9291..bbab69d2 100644 --- a/src/types/json.d.ts +++ b/src/types/json.d.ts @@ -1,4 +1,4 @@ declare module '*.json' { - const value: any; - export default value; + const value: any; + export default value; } diff --git a/test/e2e/api/info.test.ts b/test/e2e/api/info.test.ts index ddeb1ab0..d88f7789 100644 --- a/test/e2e/api/info.test.ts +++ b/test/e2e/api/info.test.ts @@ -5,25 +5,25 @@ import { bootstrapApp, BootstrapSettings } from '../utils/bootstrap'; describe('/api', () => { - // ------------------------------------------------------------------------- - // Setup up - // ------------------------------------------------------------------------- - - let settings: BootstrapSettings; - beforeAll(async () => settings = await bootstrapApp()); - - // ------------------------------------------------------------------------- - // Test cases - // ------------------------------------------------------------------------- - - test('GET: / should return the api-version', async (done) => { - const response = await request(settings.app) - .get('/api') - .expect('Content-Type', /json/) - .expect(200); - - expect(response.body.version).toBe(env.app.version); - done(); - }); + // ------------------------------------------------------------------------- + // Setup up + // ------------------------------------------------------------------------- + + let settings: BootstrapSettings; + beforeAll(async () => settings = await bootstrapApp()); + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + test('GET: / should return the api-version', async (done) => { + const response = await request(settings.app) + .get('/api') + .expect('Content-Type', /json/) + .expect(200); + + expect(response.body.version).toBe(env.app.version); + done(); + }); }); diff --git a/test/e2e/api/sampleUsers.test.ts b/test/e2e/api/sampleUsers.test.ts new file mode 100644 index 00000000..bf94448a --- /dev/null +++ b/test/e2e/api/sampleUsers.test.ts @@ -0,0 +1,65 @@ +import * as nock from 'nock'; +import request from 'supertest'; +import { runSeed } from 'typeorm-seeding'; + +import { SampleUser } from '../../../src/api/models/SampleUser'; +import { CreateSampleBruce } from '../../../src/database/seeds/CreateSampleBruce'; +import { closeDatabase } from '../../utils/database'; +import { BootstrapSettings } from '../utils/bootstrap'; +import { prepareServer } from '../utils/server'; + +describe('/api/users', () => { + + let bruce: SampleUser; + let bruceAuthorization: string; + let settings: BootstrapSettings; + + // ------------------------------------------------------------------------- + // Setup up + // ------------------------------------------------------------------------- + + beforeAll(async () => { + settings = await prepareServer({ migrate: true }); + bruce = await runSeed(CreateSampleBruce); + bruceAuthorization = Buffer.from(`${bruce.username}:1234`).toString('base64'); + }); + + // ------------------------------------------------------------------------- + // Tear down + // ------------------------------------------------------------------------- + + afterAll(async () => { + nock.cleanAll(); + await closeDatabase(settings.connection); + }); + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + test('GET: / should return a list of users', async (done) => { + const response = await request(settings.app) + .get('/api/users') + .set('Authorization', `Basic ${bruceAuthorization}`) + .expect('Content-Type', /json/) + .expect(200); + + expect(response.body.length).toBe(1); + done(); + }); + + test('GET: /:id should return bruce', async (done) => { + const response = await request(settings.app) + .get(`/api/users/${bruce.id}`) + .set('Authorization', `Basic ${bruceAuthorization}`) + .expect('Content-Type', /json/) + .expect(200); + + expect(response.body.id).toBe(bruce.id); + expect(response.body.firstName).toBe(bruce.firstName); + expect(response.body.lastName).toBe(bruce.lastName); + expect(response.body.email).toBe(bruce.email); + done(); + }); + +}); diff --git a/test/e2e/api/users.test.ts b/test/e2e/api/users.test.ts deleted file mode 100644 index 1f71df9b..00000000 --- a/test/e2e/api/users.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as nock from 'nock'; -import request from 'supertest'; -import { runSeed } from 'typeorm-seeding'; - -import { User } from '../../../src/api/models/User'; -import { CreateBruce } from '../../../src/database/seeds/CreateBruce'; -import { closeDatabase } from '../../utils/database'; -import { BootstrapSettings } from '../utils/bootstrap'; -import { prepareServer } from '../utils/server'; - -describe('/api/users', () => { - - let bruce: User; - let bruceAuthorization: string; - let settings: BootstrapSettings; - - // ------------------------------------------------------------------------- - // Setup up - // ------------------------------------------------------------------------- - - beforeAll(async () => { - settings = await prepareServer({ migrate: true }); - bruce = await runSeed(CreateBruce); - bruceAuthorization = Buffer.from(`${bruce.username}:1234`).toString('base64'); - }); - - // ------------------------------------------------------------------------- - // Tear down - // ------------------------------------------------------------------------- - - afterAll(async () => { - nock.cleanAll(); - await closeDatabase(settings.connection); - }); - - // ------------------------------------------------------------------------- - // Test cases - // ------------------------------------------------------------------------- - - test('GET: / should return a list of users', async (done) => { - const response = await request(settings.app) - .get('/api/users') - .set('Authorization', `Basic ${bruceAuthorization}`) - .expect('Content-Type', /json/) - .expect(200); - - expect(response.body.length).toBe(1); - done(); - }); - - test('GET: /:id should return bruce', async (done) => { - const response = await request(settings.app) - .get(`/api/users/${bruce.id}`) - .set('Authorization', `Basic ${bruceAuthorization}`) - .expect('Content-Type', /json/) - .expect(200); - - expect(response.body.id).toBe(bruce.id); - expect(response.body.firstName).toBe(bruce.firstName); - expect(response.body.lastName).toBe(bruce.lastName); - expect(response.body.email).toBe(bruce.email); - done(); - }); - -}); diff --git a/test/e2e/utils/bootstrap.ts b/test/e2e/utils/bootstrap.ts index 38dc310f..8fb2e56b 100644 --- a/test/e2e/utils/bootstrap.ts +++ b/test/e2e/utils/bootstrap.ts @@ -8,28 +8,28 @@ import { expressLoader } from '../../../src/loaders/expressLoader'; import { homeLoader } from '../../../src/loaders/homeLoader'; import { iocLoader } from '../../../src/loaders/iocLoader'; import { winstonLoader } from '../../../src/loaders/winstonLoader'; -import { typeormLoader } from '../utils/typeormLoader'; +import { typeormLoader } from './typeormLoader'; export interface BootstrapSettings { - app: Application; - server: http.Server; - connection: Connection; + app: Application; + server: http.Server; + connection: Connection; } export const bootstrapApp = async (): Promise => { - const framework = await bootstrapMicroframework({ - loaders: [ - winstonLoader, - iocLoader, - eventDispatchLoader, - typeormLoader, - expressLoader, - homeLoader, - ], - }); - return { - app: framework.settings.getData('express_app') as Application, - server: framework.settings.getData('express_server') as http.Server, - connection: framework.settings.getData('connection') as Connection, - } as BootstrapSettings; + const framework = await bootstrapMicroframework({ + loaders: [ + winstonLoader, + iocLoader, + eventDispatchLoader, + typeormLoader, + expressLoader, + homeLoader, + ], + }); + return { + app: framework.settings.getData('express_app') as Application, + server: framework.settings.getData('express_server') as http.Server, + connection: framework.settings.getData('connection') as Connection, + } as BootstrapSettings; }; diff --git a/test/e2e/utils/server.ts b/test/e2e/utils/server.ts index 2043f159..9641eaa2 100644 --- a/test/e2e/utils/server.ts +++ b/test/e2e/utils/server.ts @@ -4,10 +4,10 @@ import { migrateDatabase } from '../../utils/database'; import { bootstrapApp } from './bootstrap'; export const prepareServer = async (options?: { migrate: boolean }) => { - const settings = await bootstrapApp(); - if (options && options.migrate) { - await migrateDatabase(settings.connection); - } - setConnection(settings.connection); - return settings; + const settings = await bootstrapApp(); + if (options && options.migrate) { + await migrateDatabase(settings.connection); + } + setConnection(settings.connection); + return settings; }; diff --git a/test/e2e/utils/typeormLoader.ts b/test/e2e/utils/typeormLoader.ts index c32e4522..e4a172af 100644 --- a/test/e2e/utils/typeormLoader.ts +++ b/test/e2e/utils/typeormLoader.ts @@ -4,9 +4,9 @@ import { createDatabaseConnection } from '../../utils/database'; export const typeormLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => { - const connection = await createDatabaseConnection(); - if (settings) { - settings.setData('connection', connection); - settings.onShutdown(() => connection.close()); - } + const connection = await createDatabaseConnection(); + if (settings) { + settings.setData('connection', connection); + settings.onShutdown(() => connection.close()); + } }; diff --git a/test/integration/PetService.test.ts b/test/integration/PetService.test.ts deleted file mode 100644 index 59ecf12f..00000000 --- a/test/integration/PetService.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Container } from 'typedi'; -import { Connection } from 'typeorm'; - -import { Pet } from '../../src/api/models/Pet'; -import { PetService } from '../../src/api/services/PetService'; -import { closeDatabase, createDatabaseConnection, migrateDatabase } from '../utils/database'; -import { configureLogger } from '../utils/logger'; - -describe('PetService', () => { - - // ------------------------------------------------------------------------- - // Setup up - // ------------------------------------------------------------------------- - - let connection: Connection; - beforeAll(async () => { - configureLogger(); - connection = await createDatabaseConnection(); - }); - beforeEach(() => migrateDatabase(connection)); - - // ------------------------------------------------------------------------- - // Tear down - // ------------------------------------------------------------------------- - - afterAll(() => closeDatabase(connection)); - - // ------------------------------------------------------------------------- - // Test cases - // ------------------------------------------------------------------------- - - test('should create a new pet in the database', async (done) => { - const pet = new Pet(); - pet.id = 'xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx'; - pet.name = 'test'; - pet.age = 1; - const service = Container.get(PetService); - const resultCreate = await service.create(pet); - expect(resultCreate.name).toBe(pet.name); - expect(resultCreate.age).toBe(pet.age); - - const resultFind = await service.findOne(resultCreate.id); - if (resultFind) { - expect(resultFind.name).toBe(pet.name); - expect(resultFind.age).toBe(pet.age); - } else { - fail('Could not find pet'); - } - done(); - }); - -}); diff --git a/test/integration/SamplePetService.test.ts b/test/integration/SamplePetService.test.ts new file mode 100644 index 00000000..7d30b694 --- /dev/null +++ b/test/integration/SamplePetService.test.ts @@ -0,0 +1,52 @@ +import { Container } from 'typedi'; +import { Connection } from 'typeorm'; + +import { SamplePet } from '../../src/api/models/SamplePet'; +import { SamplePetService } from '../../src/api/services/SamplePetService'; +import { closeDatabase, createDatabaseConnection, migrateDatabase } from '../utils/database'; +import { configureLogger } from '../utils/logger'; + +describe('SamplePetService', () => { + + // ------------------------------------------------------------------------- + // Setup up + // ------------------------------------------------------------------------- + + let connection: Connection; + beforeAll(async () => { + configureLogger(); + connection = await createDatabaseConnection(); + }); + beforeEach(() => migrateDatabase(connection)); + + // ------------------------------------------------------------------------- + // Tear down + // ------------------------------------------------------------------------- + + afterAll(() => closeDatabase(connection)); + + // ------------------------------------------------------------------------- + // Test cases + // ------------------------------------------------------------------------- + + test('should create a new pet in the database', async (done) => { + const pet = new SamplePet(); + pet.id = 'xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx'; + pet.name = 'test'; + pet.age = 1; + const service = Container.get(SamplePetService); + const resultCreate = await service.create(pet); + expect(resultCreate.name).toBe(pet.name); + expect(resultCreate.age).toBe(pet.age); + + const resultFind = await service.findOne(resultCreate.id); + if (resultFind) { + expect(resultFind.name).toBe(pet.name); + expect(resultFind.age).toBe(pet.age); + } else { + fail('Could not find pet'); + } + done(); + }); + +}); diff --git a/test/unit/auth/AuthService.test.ts b/test/unit/auth/AuthService.test.ts index 5fd2b0fa..7d319568 100644 --- a/test/unit/auth/AuthService.test.ts +++ b/test/unit/auth/AuthService.test.ts @@ -1,6 +1,6 @@ import { Request } from 'express'; import MockExpressRequest from 'mock-express-request'; -import { User } from 'src/api/models/User'; +import { SampleUser } from 'src/api/models/SampleUser'; import { AuthService } from '../../../src/auth/AuthService'; import { LogMock } from '../lib/LogMock'; @@ -8,48 +8,48 @@ import { RepositoryMock } from '../lib/RepositoryMock'; describe('AuthService', () => { - let authService: AuthService; - let userRepository: RepositoryMock; - let log: LogMock; - beforeEach(() => { - log = new LogMock(); - userRepository = new RepositoryMock(); - authService = new AuthService(log, userRepository as any); + let authService: AuthService; + let userRepository: RepositoryMock; + let log: LogMock; + beforeEach(() => { + log = new LogMock(); + userRepository = new RepositoryMock(); + authService = new AuthService(log, userRepository as any); + }); + + describe('parseTokenFromRequest', () => { + test('Should return the credentials of the basic authorization header', () => { + const base64 = Buffer.from(`bruce:1234`).toString('base64'); + const req: Request = new MockExpressRequest({ + headers: { + Authorization: `Basic ${base64}`, + }, + }); + const credentials = authService.parseBasicAuthFromRequest(req); + expect(credentials.username).toBe('bruce'); + expect(credentials.password).toBe('1234'); }); - describe('parseTokenFromRequest', () => { - test('Should return the credentials of the basic authorization header', () => { - const base64 = Buffer.from(`bruce:1234`).toString('base64'); - const req: Request = new MockExpressRequest({ - headers: { - Authorization: `Basic ${base64}`, - }, - }); - const credentials = authService.parseBasicAuthFromRequest(req); - expect(credentials.username).toBe('bruce'); - expect(credentials.password).toBe('1234'); - }); - - test('Should return undefined if there is no basic authorization header', () => { - const req: Request = new MockExpressRequest({ - headers: {}, - }); - const token = authService.parseBasicAuthFromRequest(req); - expect(token).toBeUndefined(); - expect(log.infoMock).toBeCalledWith('No credentials provided by the client', []); - }); - - test('Should return undefined if there is a invalid basic authorization header', () => { - const req: Request = new MockExpressRequest({ - headers: { - Authorization: 'Basic 1234', - }, - }); - const token = authService.parseBasicAuthFromRequest(req); - expect(token).toBeUndefined(); - expect(log.infoMock).toBeCalledWith('No credentials provided by the client', []); - }); + test('Should return undefined if there is no basic authorization header', () => { + const req: Request = new MockExpressRequest({ + headers: {}, + }); + const token = authService.parseBasicAuthFromRequest(req); + expect(token).toBeUndefined(); + expect(log.infoMock).toBeCalledWith('No credentials provided by the client', []); + }); + test('Should return undefined if there is a invalid basic authorization header', () => { + const req: Request = new MockExpressRequest({ + headers: { + Authorization: 'Basic 1234', + }, + }); + const token = authService.parseBasicAuthFromRequest(req); + expect(token).toBeUndefined(); + expect(log.infoMock).toBeCalledWith('No credentials provided by the client', []); }); + }); + }); diff --git a/test/unit/lib/EventDispatcherMock.ts b/test/unit/lib/EventDispatcherMock.ts index baa640a6..dc1b3fd7 100644 --- a/test/unit/lib/EventDispatcherMock.ts +++ b/test/unit/lib/EventDispatcherMock.ts @@ -1,9 +1,9 @@ export class EventDispatcherMock { - public dispatchMock = jest.fn(); + public dispatchMock = jest.fn(); - public dispatch(...args: any[]): void { - this.dispatchMock(args); - } + public dispatch(...args: any[]): void { + this.dispatchMock(args); + } } diff --git a/test/unit/lib/LogMock.ts b/test/unit/lib/LogMock.ts index 9ea7fd42..136f31db 100644 --- a/test/unit/lib/LogMock.ts +++ b/test/unit/lib/LogMock.ts @@ -2,25 +2,25 @@ import { Logger } from '../../../src/lib/logger'; export class LogMock extends Logger { - public debugMock = jest.fn(); - public infoMock = jest.fn(); - public warnMock = jest.fn(); - public errorMock = jest.fn(); + public debugMock = jest.fn(); + public infoMock = jest.fn(); + public warnMock = jest.fn(); + public errorMock = jest.fn(); - public debug(message: string, ...args: any[]): void { - this.debugMock(message, args); - } + public debug(message: string, ...args: any[]): void { + this.debugMock(message, args); + } - public info(message: string, ...args: any[]): void { - this.infoMock(message, args); - } + public info(message: string, ...args: any[]): void { + this.infoMock(message, args); + } - public warn(message: string, ...args: any[]): void { - this.warnMock(message, args); - } + public warn(message: string, ...args: any[]): void { + this.warnMock(message, args); + } - public error(message: string, ...args: any[]): void { - this.errorMock(message, args); - } + public error(message: string, ...args: any[]): void { + this.errorMock(message, args); + } } diff --git a/test/unit/lib/RepositoryMock.ts b/test/unit/lib/RepositoryMock.ts index 5ae85a33..12bbd810 100644 --- a/test/unit/lib/RepositoryMock.ts +++ b/test/unit/lib/RepositoryMock.ts @@ -1,31 +1,31 @@ export class RepositoryMock { - public one: T; - public list: T[]; + public one: T; + public list: T[]; - public findMock = jest.fn(); - public findOneMock = jest.fn(); - public saveMock = jest.fn(); - public deleteMock = jest.fn(); + public findMock = jest.fn(); + public findOneMock = jest.fn(); + public saveMock = jest.fn(); + public deleteMock = jest.fn(); - public find(...args: any[]): Promise { - this.findMock(args); - return Promise.resolve(this.list); - } + public find(...args: any[]): Promise { + this.findMock(args); + return Promise.resolve(this.list); + } - public findOne(...args: any[]): Promise { - this.findOneMock(args); - return Promise.resolve(this.one); - } + public findOne(...args: any[]): Promise { + this.findOneMock(args); + return Promise.resolve(this.one); + } - public save(value: T, ...args: any[]): Promise { - this.saveMock(value, args); - return Promise.resolve(value); - } + public save(value: T, ...args: any[]): Promise { + this.saveMock(value, args); + return Promise.resolve(value); + } - public delete(value: T, ...args: any[]): Promise { - this.deleteMock(value, args); - return Promise.resolve(value); - } + public delete(value: T, ...args: any[]): Promise { + this.deleteMock(value, args); + return Promise.resolve(value); + } } diff --git a/test/unit/middlewares/ErrorHandlerMiddleware.test.ts b/test/unit/middlewares/ErrorHandlerMiddleware.test.ts index 53a65ba2..8f2630fd 100644 --- a/test/unit/middlewares/ErrorHandlerMiddleware.test.ts +++ b/test/unit/middlewares/ErrorHandlerMiddleware.test.ts @@ -6,33 +6,33 @@ import { LogMock } from '../lib/LogMock'; describe('ErrorHandlerMiddleware', () => { - let log; - let middleware; - let err; - let res; - beforeEach(() => { - log = new LogMock(); - middleware = new ErrorHandlerMiddleware(log); - res = new MockExpressResponse(); - err = new HttpError(400, 'Test Error'); - }); + let log; + let middleware; + let err; + let res; + beforeEach(() => { + log = new LogMock(); + middleware = new ErrorHandlerMiddleware(log); + res = new MockExpressResponse(); + err = new HttpError(400, 'Test Error'); + }); - test('Should not print stack out in production', () => { - middleware.isProduction = true; - middleware.error(err, undefined, res, undefined); - const json = res._getJSON(); - expect(json.name).toBe(err.name); - expect(json.message).toBe(err.message); - expect(log.errorMock).toHaveBeenCalledWith(err.name, [err.message]); - }); + test('Should not print stack out in production', () => { + middleware.isProduction = true; + middleware.error(err, undefined, res, undefined); + const json = res._getJSON(); + expect(json.name).toBe(err.name); + expect(json.message).toBe(err.message); + expect(log.errorMock).toHaveBeenCalledWith(err.name, [err.message]); + }); - test('Should print stack out in development', () => { - middleware.isProduction = false; - middleware.error(err, undefined, res, undefined); - const json = res._getJSON(); - expect(json.name).toBe(err.name); - expect(json.message).toBe(err.message); - expect(log.errorMock).toHaveBeenCalled(); - }); + test('Should print stack out in development', () => { + middleware.isProduction = false; + middleware.error(err, undefined, res, undefined); + const json = res._getJSON(); + expect(json.name).toBe(err.name); + expect(json.message).toBe(err.message); + expect(log.errorMock).toHaveBeenCalled(); + }); }); diff --git a/test/unit/services/SampleUserService.test.ts b/test/unit/services/SampleUserService.test.ts new file mode 100644 index 00000000..cae8b07b --- /dev/null +++ b/test/unit/services/SampleUserService.test.ts @@ -0,0 +1,41 @@ +import { SampleUser } from '../../../src/api/models/SampleUser'; +import { SampleUserService } from '../../../src/api/services/SampleUserService'; +import { events } from '../../../src/api/subscribers/events'; +import { EventDispatcherMock } from '../lib/EventDispatcherMock'; +import { LogMock } from '../lib/LogMock'; +import { RepositoryMock } from '../lib/RepositoryMock'; + +describe('SampleUserService', () => { + + test('Find should return a list of users', async (done) => { + const log = new LogMock(); + const repo = new RepositoryMock(); + const ed = new EventDispatcherMock(); + const user = new SampleUser(); + user.id = '1'; + user.firstName = 'John'; + user.lastName = 'Doe'; + user.email = 'john.doe@test.com'; + repo.list = [user]; + const userService = new SampleUserService(repo as any, ed as any, log); + const list = await userService.find(); + expect(list[0].firstName).toBe(user.firstName); + done(); + }); + + test('Create should dispatch subscribers', async (done) => { + const log = new LogMock(); + const repo = new RepositoryMock(); + const ed = new EventDispatcherMock(); + const user = new SampleUser(); + user.id = '1'; + user.firstName = 'John'; + user.lastName = 'Doe'; + user.email = 'john.doe@test.com'; + const userService = new SampleUserService(repo as any, ed as any, log); + const newUser = await userService.create(user); + expect(ed.dispatchMock).toBeCalledWith([events.sampleUser.created, newUser]); + done(); + }); + +}); diff --git a/test/unit/services/UserService.test.ts b/test/unit/services/UserService.test.ts deleted file mode 100644 index 3a1a04ba..00000000 --- a/test/unit/services/UserService.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { User } from '../../../src/api/models/User'; -import { UserService } from '../../../src/api/services/UserService'; -import { events } from '../../../src/api/subscribers/events'; -import { EventDispatcherMock } from '../lib/EventDispatcherMock'; -import { LogMock } from '../lib/LogMock'; -import { RepositoryMock } from '../lib/RepositoryMock'; - -describe('UserService', () => { - - test('Find should return a list of users', async (done) => { - const log = new LogMock(); - const repo = new RepositoryMock(); - const ed = new EventDispatcherMock(); - const user = new User(); - user.id = '1'; - user.firstName = 'John'; - user.lastName = 'Doe'; - user.email = 'john.doe@test.com'; - repo.list = [user]; - const userService = new UserService(repo as any, ed as any, log); - const list = await userService.find(); - expect(list[0].firstName).toBe(user.firstName); - done(); - }); - - test('Create should dispatch subscribers', async (done) => { - const log = new LogMock(); - const repo = new RepositoryMock(); - const ed = new EventDispatcherMock(); - const user = new User(); - user.id = '1'; - user.firstName = 'John'; - user.lastName = 'Doe'; - user.email = 'john.doe@test.com'; - const userService = new UserService(repo as any, ed as any, log); - const newUser = await userService.create(user); - expect(ed.dispatchMock).toBeCalledWith([events.user.created, newUser]); - done(); - }); - -}); diff --git a/test/unit/validations/SampleUserValidations.test.ts b/test/unit/validations/SampleUserValidations.test.ts new file mode 100644 index 00000000..416aa597 --- /dev/null +++ b/test/unit/validations/SampleUserValidations.test.ts @@ -0,0 +1,46 @@ +import { validate } from 'class-validator'; + +import { SampleUser } from '../../../src/api/models/SampleUser'; + +describe('UserValidations', () => { + + test('SampleUser should always have a first name', async (done) => { + const user = new SampleUser(); + const errorsOne = await validate(user); + user.firstName = 'TestName'; + const errorsTwo = await validate(user); + expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); + done(); + }); + + test('SampleUser should always have a last name', async (done) => { + const user = new SampleUser(); + const errorsOne = await validate(user); + user.lastName = 'TestName'; + const errorsTwo = await validate(user); + expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); + done(); + }); + + test('SampleUser should always have a email', async (done) => { + const user = new SampleUser(); + const errorsOne = await validate(user); + user.email = 'test@test.com'; + const errorsTwo = await validate(user); + expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); + done(); + }); + + test('SampleUser validation should succeed with all required fields', async (done) => { + const user = new SampleUser(); + user.firstName = 'TestName'; + user.lastName = 'TestName'; + user.email = 'test@test.com'; + user.username = 'test'; + user.password = '1234'; + const errors = await validate(user); + expect(errors.length).toEqual(0); + done(); + }); + +}); diff --git a/test/unit/validations/UserValidations.test.ts b/test/unit/validations/UserValidations.test.ts deleted file mode 100644 index cebbdf00..00000000 --- a/test/unit/validations/UserValidations.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { validate } from 'class-validator'; - -import { User } from '../../../src/api/models/User'; - -describe('UserValidations', () => { - - test('User should always have a first name', async (done) => { - const user = new User(); - const errorsOne = await validate(user); - user.firstName = 'TestName'; - const errorsTwo = await validate(user); - expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); - done(); - }); - - test('User should always have a last name', async (done) => { - const user = new User(); - const errorsOne = await validate(user); - user.lastName = 'TestName'; - const errorsTwo = await validate(user); - expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); - done(); - }); - - test('User should always have a email', async (done) => { - const user = new User(); - const errorsOne = await validate(user); - user.email = 'test@test.com'; - const errorsTwo = await validate(user); - expect(errorsOne.length).toBeGreaterThan(errorsTwo.length); - done(); - }); - - test('User validation should succeed with all required fields', async (done) => { - const user = new User(); - user.firstName = 'TestName'; - user.lastName = 'TestName'; - user.email = 'test@test.com'; - user.username = 'test'; - user.password = '1234'; - const errors = await validate(user); - expect(errors.length).toEqual(0); - done(); - }); - -}); diff --git a/test/utils/database.ts b/test/utils/database.ts index 84739e2f..15b34332 100644 --- a/test/utils/database.ts +++ b/test/utils/database.ts @@ -6,27 +6,27 @@ import { env } from '../../src/env'; declare type LoggerOptions = boolean | 'all' | Array<('query' | 'schema' | 'error' | 'warn' | 'info' | 'log' | 'migration')>; export const createDatabaseConnection = async (): Promise => { - useContainer(Container); - const connection = await createConnection({ - type: env.db.type as any, // See createConnection options for valid types - database: env.db.database, - logging: env.db.logging as LoggerOptions, - entities: env.app.dirs.entities, - migrations: env.app.dirs.migrations, - }); - return connection; + useContainer(Container); + const connection = await createConnection({ + type: env.db.type as any, // See createConnection options for valid types + database: env.db.database, + logging: env.db.logging as LoggerOptions, + entities: env.app.dirs.entities, + migrations: env.app.dirs.migrations, + }); + return connection; }; export const synchronizeDatabase = async (connection: Connection) => { - await connection.dropDatabase(); - return connection.synchronize(true); + await connection.dropDatabase(); + return connection.synchronize(true); }; export const migrateDatabase = async (connection: Connection) => { - await connection.dropDatabase(); - return connection.runMigrations(); + await connection.dropDatabase(); + return connection.runMigrations(); }; export const closeDatabase = (connection: Connection) => { - return connection.close(); + return connection.close(); }; diff --git a/test/utils/logger.ts b/test/utils/logger.ts index a3ce58e9..1fb3c1ef 100644 --- a/test/utils/logger.ts +++ b/test/utils/logger.ts @@ -1,12 +1,12 @@ import { configure, transports } from 'winston'; export const configureLogger = () => { - configure({ - transports: [ - new transports.Console({ - level: 'none', - handleExceptions: false, - }), - ], - }); + configure({ + transports: [ + new transports.Console({ + level: 'none', + handleExceptions: false, + }), + ], + }); }; diff --git a/tsconfig.json b/tsconfig.json index 6d1e19dd..9341afd9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,41 +1,41 @@ { - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "pretty": true, - "sourceMap": true, - "outDir": "dist", - "importHelpers": true, - "strict": true, - "noImplicitAny": false, - "strictNullChecks": false, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": true, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", - "baseUrl": ".", - "allowSyntheticDefaultImports": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "lib": [ - "es5", - "es6", - "dom", - "es2015.core", - "es2015.collection", - "es2015.generator", - "es2015.iterable", - "es2015.promise", - "es2015.proxy", - "es2015.reflect", - "es2015.symbol", - "es2015.symbol.wellknown", - "esnext.asynciterable" - ] - } + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "pretty": true, + "sourceMap": true, + "outDir": "dist", + "importHelpers": true, + "strict": true, + "noImplicitAny": false, + "strictNullChecks": false, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "baseUrl": ".", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": [ + "es5", + "es6", + "dom", + "es2015.core", + "es2015.collection", + "es2015.generator", + "es2015.iterable", + "es2015.promise", + "es2015.proxy", + "es2015.reflect", + "es2015.symbol", + "es2015.symbol.wellknown", + "esnext.asynciterable" + ] + } } diff --git a/tslint.json b/tslint.json index e62ca413..fdeb77af 100644 --- a/tslint.json +++ b/tslint.json @@ -1,129 +1,127 @@ { - "extends": "tslint:recommended", - "rules": { - "max-classes-per-file": false, - "max-line-length": [ - true, - 160 - ], - "no-unnecessary-initializer": false, - "no-var-requires": true, - "no-null-keyword": true, - "no-consecutive-blank-lines": true, - "quotemark": [ - true, - "single", - "avoid-escape" - ], - "interface-name": false, - "no-empty-interface": false, - "no-namespace": false, - "ordered-imports": false, - "object-literal-sort-keys": false, - "arrow-parens": false, - "member-ordering": [ - true, - { - "order": [ - "public-static-field", - "public-static-method", - "protected-static-field", - "protected-static-method", - "private-static-field", - "private-static-method", - "public-instance-field", - "protected-instance-field", - "private-instance-field", - "public-constructor", - "protected-constructor", - "private-constructor", - "public-instance-method", - "protected-instance-method", - "private-instance-method" - ] - } - ], - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-switch-case-fall-through": true, - "typedef": [ - true, - "call-signature", - "parameter" - ], - "trailing-comma": [ - true, - { - "multiline": { - "objects": "always", - "arrays": "always", - "functions": "never", - "typeLiterals": "ignore" - }, - "singleline": "never" - } - ], - "align": [ - true, - "parameters" - ], - "class-name": true, - "curly": true, - "eofline": true, - "jsdoc-format": true, - "member-access": true, - "no-arg": true, - "no-construct": true, - "no-duplicate-variable": true, - "no-empty": true, - "no-eval": true, - "no-internal-module": true, - "no-string-literal": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-finally", - "check-whitespace" - ], - "semicolon": true, - "switch-default": true, - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" + "extends": "tslint:recommended", + "rules": { + "max-classes-per-file": false, + "max-line-length": false, + "no-unnecessary-initializer": false, + "no-var-requires": true, + "no-null-keyword": true, + "no-consecutive-blank-lines": true, + "quotemark": [ + true, + "single", + "avoid-escape" + ], + "interface-name": false, + "no-empty-interface": false, + "no-namespace": false, + "ordered-imports": false, + "object-literal-sort-keys": false, + "object-literal-shorthand": false, + "arrow-parens": false, + "member-ordering": [ + true, + { + "order": [ + "public-static-field", + "public-static-method", + "protected-static-field", + "protected-static-method", + "private-static-field", + "private-static-method", + "public-instance-field", + "protected-instance-field", + "private-instance-field", + "public-constructor", + "protected-constructor", + "private-constructor", + "public-instance-method", + "protected-instance-method", + "private-instance-method" ] - } + } + ], + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-switch-case-fall-through": true, + "typedef": [ + true, + "call-signature", + "parameter" + ], + "trailing-comma": [ + true, + { + "multiline": { + "objects": "always", + "arrays": "always", + "functions": "ignore", + "typeLiterals": "ignore" + }, + "singleline": "never" + } + ], + "align": [ + true, + "parameters" + ], + "class-name": true, + "curly": true, + "eofline": true, + "jsdoc-format": true, + "member-access": true, + "no-arg": true, + "no-construct": true, + "no-duplicate-variable": true, + "no-empty": true, + "no-eval": true, + "no-internal-module": true, + "no-string-literal": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-finally", + "check-whitespace" + ], + "semicolon": true, + "switch-default": true, + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } } diff --git a/yarn.lock b/yarn.lock index efb86777..179fde65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6035,10 +6035,10 @@ typeorm@^0.2.5, typeorm@^0.2.7: yargonaut "^1.1.2" yargs "^11.1.0" -typescript@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@^3.7.5: + version "3.7.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" + integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== uglify-js@^3.1.4: version "3.4.9"