├── .gitignore ├── Features.md ├── Readme.md ├── UpcomingFeatures.md ├── sql └── Coreservice.sql └── src ├── .vs └── config │ └── applicationhost.config ├── Core.Services.Tests.Automation ├── .vscode │ └── launch.json ├── Core.Services.Tests.Automation.njsproj ├── README.md ├── package.json └── tests │ ├── features │ ├── service.feature │ ├── step_definitions │ │ ├── config.js │ │ ├── hooks.js │ │ └── stepDefinition.js │ └── support │ │ └── world.js │ └── report ├── Core.Services.Tests.Unit ├── Core.Services.Tests.Unit.csproj └── ServiceControllerTests.cs ├── Core.Services.sln ├── Core.Services ├── .dockerignore ├── Areas │ └── V1 │ │ ├── Controlllers │ │ ├── AddEmployee.cs │ │ ├── GetEmployeeById.cs │ │ ├── GetEmployeeList.cs │ │ ├── RemoveEmployee.cs │ │ └── _ServiceController.cs │ │ └── Models │ │ ├── Requests │ │ └── AddEmployeeRequest.cs │ │ └── Responses │ │ ├── AddEmployeeResponse.cs │ │ ├── GetEmployeeResponse.cs │ │ ├── GetEmployeesListResponse.cs │ │ └── RemoveEmployeeResponse.cs ├── Configurations │ └── AppSettings.cs ├── Controllers │ └── VersionController.cs ├── Core.Services.csproj ├── Dockerfile ├── Entities │ ├── Constants.cs │ ├── Employee.cs │ ├── ResponseMessage.cs │ ├── VersionDocument.cs │ ├── VersionModel.cs │ └── VersionStatus.cs ├── Filters │ ├── CustomAuthorize.cs │ └── CustomExceptionFilter.cs ├── Helpers │ └── Helper.cs ├── Logger │ └── LogResponseMiddleware.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── Database │ │ ├── DatabaseRepository.cs │ │ └── IDatabaseRepository.cs │ └── Models │ │ └── DBErrorResponse.cs ├── Startup.cs ├── appsettings.Development.json ├── appsettings.Production.json ├── appsettings.QA.json ├── appsettings.Staging.json └── appsettings.json ├── docker-compose.ci.build.yml ├── docker-compose.dcproj ├── docker-compose.override.yml └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | Core.Services/.vs/ 2 | Core.Services/Core.Services/bin/ 3 | Core.Services/Core.Services/newrelic/ 4 | Core.Services/Core.Services/obj/ 5 | Core.Services/obj/ 6 | Core.Services/Core.Services.Tests.Automation/.vscode/ 7 | Core.Services/Core.Services.Tests.Automation/node_modules/ 8 | Core.Services/Core.Services.Tests.Automation/obj/ 9 | src/Core.Services/obj/ 10 | src/obj/ 11 | src/Core.Services/newrelic/ 12 | src/Core.Services/bin/ 13 | src/Core.Services.Tests.Automation/node_modules/ 14 | src/Core.Services.Tests.Automation/obj/ 15 | src/.vs/Core.Services/v15/ 16 | src/Core.Services.Tests.Unit/bin/ 17 | src/Core.Services.Tests.Unit/obj/ 18 | src/.vs/ 19 | -------------------------------------------------------------------------------- /Features.md: -------------------------------------------------------------------------------- 1 | # This .NetCore Microservice Application contains below features: 2 | * Authentication (Basic Key) 3 | * Exception handling 4 | * Database connectivity 5 | * Database script 6 | * Docker 7 | * Logging: 8 | 1. Docker logging (Kitematic) 9 | * Monitoring: 10 | 1. NewRelic 11 | * Tests: 12 | 1. Cucumber Automation Tests 13 | 2. Unit Tests with Mock 14 | 15 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Prerequisite: 2 | 3 | * Windows 10 4 | * Visual Studio 2017 5 | * .Net Core SDK : Download and install from 6 | 1. https://www.microsoft.com/net/learn/get-started/windows#windowscmd 7 | * Docker : https://docs.docker.com/docker-for-windows/install/#download-docker-for-windows 8 | * Docker Toolbox :https://docs.docker.com/toolbox/toolbox_install_windows/ 9 | 1. It will provide Kitematic .It's a Docker UI for docker containers and logs. 10 | 11 | ## Database 12 | If you want to use your local database then Execute Database script "/sql/Coreservice.sql" : 13 | * It will create coreservices databse with required tables and sps 14 | * Update connection string with your database connections. 15 | 16 | ## NewRelic 17 | * Case1: If you donot want to use NewRelic then comment below three lines in DockerFile : ../src/Core.Services/Dockerfile : 18 | 1. ARG NewRelic=./newrelic 19 | 2. COPY $NewRelic ./newrelic 20 | 3. RUN dpkg -i ./newrelic/newrelic-netcore20-agent*.deb 21 | 22 | * Case2: In you want to use NewRelic monitoring: 23 | * Download below from http://download.newrelic.com/dot_net_agent/core_20/current 24 | 1. newrelic-netcore20-agent_8.0.0.0_amd64.deb 25 | * Create a folder named "newrelic" inside ../src/Core.Services 26 | * Add below line in .dockerignore file : 27 | !newrelic 28 | * Register into Newrelic and get NEW_RELIC_LICENSE_KEY 29 | * Replace this NEW_RELIC_LICENSE_KEY value in dockerfile 30 | 31 | 32 | ## Run application using below steps: 33 | * Open command promt and go to ../src/Core.Services 34 | * dotnet restore 35 | * dotnet build 36 | * dotnet publish -o obj/Docker/publish 37 | * docker build -t coreimage . 38 | * See images using command "docker images",it will list your image "coreimage" also 39 | * docker run -d -p 28601:8601 --name core1 --env ASPNETCORE_ENVIRONMENT=QA coreimage 40 | 1. d => detached mode 41 | 2. p => host protocol and here you will acees api by localhost:28601 42 | 3. name => container name 43 | 4. env => Environmentname like QA/Development/Staging/PROD for running transformation 44 | * See conatainers running using command "docker ps",it will lsit your container "core1" also 45 | 46 | ## Access Apis 47 | 48 | * http://localhost:28601/service/version 49 | * To access below apis ,you need to pass authentication key in header 50 | apikey:67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2 51 | 1. To GetList of Employees: 52 | http://localhost:28601/service/v1/ 53 | 2. To GetList of Employees: 54 | http://localhost:28601/service/v1/{id} 55 | 56 | ## Check Kitematic for logs 57 | 58 | ## To see NewRelic logs 59 | * Run docker image with NewRelic app name 60 | docker run -d -p 28601:8601 --name core1 --env ASPNETCORE_ENVIRONMENT=QA coreimage --env NEW_RELIC_APP_NAME=CoreServiceApp 61 | * Go to newrelic and click on Application and select your app "CoreServiceApp" 62 | 63 | ## Run Unit Tests 64 | 1. Open command prompt and go to unit test project : ../src/Core.Services.Tests.Unit 65 | 2. run tests using command : dotnet test 66 | -------------------------------------------------------------------------------- /UpcomingFeatures.md: -------------------------------------------------------------------------------- 1 | # Upcoming Features: 2 | 3 | ## Phase 2: 4 | * Rename repo and servicename to core-microservices-template 5 | * Tables and seed data creation as part of the migration in the source (rather than the separate SQL file) 6 | * Unit test case coverage - target for 100% 7 | * Swagger and OpenAPI Documentation 8 | * transfer this repo to 3Pillar Global Open Source (handle 3pillarlabs) 9 | 10 | ## Phase 3: 11 | * Visual Studio Template 12 | * Create Idserver Application and Setup 13 | * Idserver integration in CustomServiceTemplate 14 | -------------------------------------------------------------------------------- /sql/Coreservice.sql: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = N'CoreServices') 2 | Create Database CoreServices 3 | GO 4 | 5 | use CoreServices 6 | GO 7 | 8 | Create table Department(DepartmentId int primary key identity(1000,1) ,Name varchar(15)) 9 | GO 10 | 11 | CREATE table Employee(EmployeeId int primary key identity(100,1),Name varchar(20),Address varchar(30),Salary int,IsActive bit,deptId int references Department) 12 | GO 13 | 14 | 15 | CREATE PROC usp_GetEmployeesList 16 | AS 17 | BEGIN 18 | SELECT EMP.EMPLOYEEID,EMP.NAME,EMP.ADDRESS,DEPT.NAME as DeptName FROM Employee EMP 19 | INNER JOIN Department DEPT ON EMP.deptId=DEPT.DepartmentId 20 | Where EMP.IsActive=1 21 | END 22 | 23 | GO 24 | CREATE PROC usp_GetEmployeeDetail 25 | @empId int 26 | AS 27 | BEGIN 28 | SELECT EMP.EMPLOYEEID,EMP.NAME,EMP.ADDRESS,EMP.SALARY,EMP.IsActive,DEPT.NAME as DeptName FROM Employee EMP 29 | INNER JOIN Department DEPT ON EMP.deptId=DEPT.DepartmentId 30 | WHERE EMP.EMPLOYEEID=@empId 31 | END 32 | 33 | GO 34 | CREATE PROC usp_AddEmployee 35 | @name varchar(20), 36 | @address varchar(30), 37 | @salary int, 38 | @deptId int 39 | AS 40 | BEGIN 41 | INSERT INTO Employee (Name,Address,Salary,isActive,deptId) 42 | VALUES(@name,@address,@salary,1,@deptId) 43 | END 44 | 45 | GO 46 | CREATE PROC usp_DeleteEmployee 47 | @empId int 48 | AS 49 | BEGIN 50 | DELETE FROM Employee WHERE EMPLOYEEID=@empId 51 | END 52 | 53 | GO 54 | 55 | INSERT INTO Department (Name) 56 | VALUES('DEVELOPMENT'),('HR'),('QA'),('SALES') 57 | 58 | INSERT INTO Employee (Name,Address,Salary,IsActive,deptId) 59 | VALUES('Ram','Noida',3743,1,1003), 60 | ('Seeta','Gurgaon',5033,1,1000), 61 | ('Reeta','Delhi',2300,1,1001), 62 | ('Mohan','Noida',6522,1,1000) 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/.vs/config/applicationhost.config: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 48 | 49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 | 95 |
96 |
97 |
98 | 99 |
100 |
101 | 102 |
103 |
104 | 105 |
106 |
107 |
108 | 109 | 110 |
111 |
112 |
113 |
114 |
115 |
116 | 117 |
118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}\\index.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/Core.Services.Tests.Automation.njsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 14.0 4 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 5 | Core.Services.Tests.Automation 6 | Hypergate.DigitalPlatform.User.Tests.Automation 7 | 8 | 9 | 10 | Debug 11 | 2.0 12 | f37735b1-251a-4ce6-a238-68ce76a1a149 13 | 14 | 15 | 16 | 17 | False 18 | 19 | 20 | . 21 | . 22 | v4.0 23 | {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 24 | ShowAllFiles 25 | false 26 | 27 | 28 | true 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/README.md: -------------------------------------------------------------------------------- 1 | cucumber-html-reporter 2 | ====================== 3 | 4 | Generate Cucumber HTML reports with pie charts 5 | > Available HTML themes: `['bootstrap', 'foundation', 'simple']` 6 | 7 | 8 | ## Preview of HTML Reports 9 | 10 | 1. [Bootstrap Theme Reports with Pie Chart][3] 11 | 2. [Foundation Theme Reports][4] 12 | 3. [Simple Theme Reports][5] 13 | 14 | 15 | ## Install 16 | 17 | ``` bash 18 | npm install 19 | ``` 20 | 21 | ## Usage 22 | 23 | ``` 24 | $ cucumberjs tests/features/ -f json:tests/report/cucumber_report.json 25 | ``` 26 | 27 | > Multiple formatter are also supported, 28 | 29 | ``` 30 | $ cucumberjs tests/features/ -f pretty -f json:tests/report/cucumber_report.json 31 | ``` 32 | 33 | > Are you using cucumber with other frameworks or running [cucumber-parallel][6]? Pass relative path of JSON file to the `options` as shown [here][7] 34 | 35 | 36 | ## Options 37 | 38 | #### `theme` 39 | Available: `['bootstrap', 'foundation', 'simple']` 40 | Type: `String` 41 | 42 | Select the Theme for HTML report. 43 | 44 | 45 | #### `jsonFile` 46 | Type: `String` 47 | 48 | Provide path of the Cucumber JSON format file 49 | 50 | #### `jsonDir` 51 | Type: `String` 52 | 53 | If you have more than one cucumber JSON files, provide the path of JSON directory. This module will create consolidated report of all Cucumber JSON files. 54 | 55 | e.g. `jsonDir: 'tests/reports'` //where _reports_ directory contains valid `*.json` files 56 | 57 | 58 | N.B.: `jsonFile` takes precedence over `jsonDir`. We recommend to use either `jsonFile` or `jsonDir` option. 59 | 60 | 61 | #### `output` 62 | Type: `String` 63 | 64 | Provide HTML output file path and name 65 | 66 | 67 | #### `reportSuiteAsScenarios` 68 | Type: `Boolean` 69 | Supported in the Bootstrap theme. 70 | 71 | `true`: Reports total number of passed/failed scenarios as HEADER. 72 | 73 | `false`: Reports total number of passed/failed features as HEADER. 74 | 75 | #### `launchReport` 76 | Type: `Boolean` 77 | 78 | Automatically launch HTML report at the end of test suite 79 | 80 | `true`: Launch HTML report in the default browser 81 | 82 | `false`: Do not launch HTML report at the end of test suite 83 | 84 | #### `ignoreBadJsonFile` 85 | Type: `Boolean` 86 | 87 | Report any bad json files found during merging json files from directory option. 88 | 89 | `true`: ignore any bad json files found and continue with remaining files to merge. 90 | 91 | `false`: Default option. Fail report generation if any bad files found during merge. 92 | 93 | #### `name` 94 | Type: `String` (optional) 95 | 96 | Custom project name. If not passed, module reads the name from projects package.json which is preferable. 97 | 98 | #### `storeScreenShots` 99 | Type: `Boolean` 100 | Default: `undefined` 101 | 102 | `true`: Stores the screenShots to the default directory. It creates a directory 'screehshot' if does not exists. 103 | 104 | `false` or `undefined` : Does not store screenShots but attaches screenShots as a step-inline images to HTML report 105 | 106 | 107 | #### `metadata` 108 | Type: `JSON` (optional) 109 | Default: `undefined` 110 | 111 | Print more data to your report, such as _browser info, platform, app info, environments_ etc. Data can be passed as JSON `key-value` pair. Reporter will parse the JSON and will show the _Key-Value_ under `Metadata` section on HTML report. Checkout the below preview HTML Report with Metadata. 112 | 113 | Pass the _Key-Value_ pair as per your need, as shown in below example, 114 | 115 | ```json 116 | 117 | metadata: { 118 | "App Version":"0.3.2", 119 | "Test Environment": "STAGING", 120 | "Browser": "Chrome 54.0.2840.98", 121 | "Platform": "Windows 10", 122 | "Parallel": "Scenarios", 123 | "Executed": "Remote" 124 | } 125 | 126 | ``` -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CoreService_Report", 3 | "version": "0.3.8", 4 | "description": "Generates Cucumber HTML reports in three different themes", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run features && npm run clean", 8 | "clean": "rm -rf tests/report/*.html tests/report/*.json tests/report/screenshot", 9 | "features": "node node_modules/cucumber/bin/cucumber tests/features/invoices.feature -f json:tests/report/cucumber_report.json", 10 | "debug": "node --debug-brk=5858 ./node_modules/.bin/cucumber-js tests/features" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/gkushang/cucumber-html-reporter.git" 15 | }, 16 | "keywords": [ 17 | "cucumber", 18 | "html", 19 | "cucumber-html-reporter", 20 | "html report", 21 | "json to html" 22 | ], 23 | "license": "MIT", 24 | "homepage": "https://github.com/gkushang/cucumber-html-reporter#readme", 25 | "dependencies": { 26 | "JSONPath": "^0.11.2", 27 | "cucumber": "^2.3.1", 28 | "dataobject-parser": "^1.1.3", 29 | "request": "^2.81.0", 30 | "stackframe": "^1.0.4" 31 | }, 32 | "devDependencies": { 33 | "cucumber-html-reporter": "^0.3.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/features/service.feature: -------------------------------------------------------------------------------- 1 | Feature: CoreService's automation tests 2 | 3 | Scenario: Get the version 4 | When I GET the version 5 | Then the http status should be 200 6 | 7 | Scenario: Get the employees detail 8 | Given I have an authorized key 9 | And path parameter id = "100" 10 | When I send a "GET" request on "v1/{id}" 11 | Then the http status should be 200 12 | And the result should not equal to null 13 | 14 | Scenario: Get the employees list 15 | Given I have an authorized key 16 | When I send a "GET" request on "v1/" 17 | Then the http status should be 200 18 | And the result should equal to null 19 | 20 | Scenario: Submit delete employee 21 | Given I have an authorized key 22 | And path parameter id = "105" 23 | When I send a "DELETE" request on "v1/{id}" 24 | Then the http status should be 200 25 | 26 | Scenario: Submit Add new employee 27 | Given I have an authorized key 28 | And body parameter Name = "Automation User1" 29 | And body parameter Address = "Delhi" 30 | And body parameter DepartmentId = "1002" 31 | And body parameter Salary = "3500" 32 | When I send a "POST" request on "v1/" 33 | Then the http status should be 200 34 | And the success should equal "true" -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/features/step_definitions/config.js: -------------------------------------------------------------------------------- 1 | exports.BASE_URL = "http://localhost:28601/service/" 2 | exports.API_KEY = "67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2" -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/features/step_definitions/hooks.js: -------------------------------------------------------------------------------- 1 | var reporter = require('cucumber-html-reporter'); 2 | 3 | var cucumber_html_option = { 4 | theme: 'bootstrap', 5 | jsonFile: 'tests/report/cucumber_report.json', 6 | output: 'tests/report/cucumber_report.html', 7 | reportSuiteAsScenarios: true, 8 | launchReport: true, 9 | name:"CoreService Automation Tests Results", 10 | metadata: { 11 | "App Version":"0.3.2", 12 | "Test Environment": "STAGING", 13 | "Browser": "Chrome 54.0.2840.98", 14 | "Platform": "Windows 10", 15 | "Parallel": "Scenarios", 16 | "Executed": "Remote" 17 | } 18 | }; 19 | 20 | 21 | var {defineSupportCode} = require('cucumber'); 22 | 23 | defineSupportCode(function({After, Before, registerHandler}) { 24 | Before(function (scenario, callback) { 25 | this.scenario = scenario; 26 | callback(); 27 | }); 28 | 29 | Before({tags: '@testPassing'}, function (scenario, callback) 30 | { 31 | callback(); 32 | }); 33 | 34 | registerHandler('AfterFeatures', function (features,callback) { 35 | reporter.generate(cucumber_html_option); 36 | setTimeout(function(){callback(); },1000); 37 | }); 38 | 39 | 40 | }); -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/features/step_definitions/stepDefinition.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var jsonPath = require('JSONPath').eval, 4 | url = require('url'), 5 | request = require('request'), 6 | config = require('./config'), 7 | DataObjectParser = require('dataobject-parser'), 8 | {defineSupportCode} = require('cucumber'); 9 | 10 | defineSupportCode(function({Given,When,Then,Before,setWorldConstructor,registerHandler, After}){ 11 | var parameters = {}, 12 | headers={}, 13 | befores =0, 14 | dParser = null; 15 | 16 | setWorldConstructor(require('../support/world.js').World); 17 | Before(function(scenario, callback) { 18 | //console.log('Test number: (' + befores + ')'); 19 | befores++; 20 | parameters = {}; 21 | headers = {}; 22 | callback(); 23 | dParser = new DataObjectParser() 24 | }); 25 | 26 | Given(/^(body|query|path|header) parameter ([^"]*) = "([^"]*)"(?:.*)$/,function(parameterType, name, data, callback) { 27 | parameters[name] = { 28 | data : data?data.replace(/"/g,""):"" 29 | ,type : parameterType 30 | }; 31 | callback() 32 | }) 33 | 34 | When(/^I send a "([^"]*)" request on "([^"]*)"(?:.*)$/, 35 | function(method,uri,callback){ 36 | send.call(this, method,uri,callback); 37 | //console.log(requestBody) 38 | }) 39 | 40 | var send = function(method, uri, callback) { 41 | var requestBody={}; 42 | for(var name in parameters) { 43 | var parameter = parameters[name]; 44 | var data = parameter.data; 45 | switch(parameter.type) { 46 | case "path": 47 | uri = uri.replace("{"+ name +"}", data); 48 | break; 49 | case "body": 50 | dParser.set(name, data); 51 | //requestBody[name] = data; 52 | break; 53 | case "query": 54 | var separator = uri.indexOf('?') !== -1 ? "&" : "?"; 55 | uri = uri + separator + name + "=" + data; 56 | break; 57 | case "header": 58 | headers[name] = parameter.data; 59 | break; 60 | } 61 | } 62 | switch(method.toLowerCase()){ 63 | case "post": 64 | //console.log(uri); 65 | this.post(uri, JSON.stringify(dParser.data()),headers, callback) 66 | break; 67 | case "delete": 68 | this.delete(uri, headers, callback) 69 | break; 70 | case "put": 71 | this.put(uri,JSON.stringify(dParser.data()), headers, callback) 72 | break; 73 | case "get": 74 | this.get(uri,headers, callback); 75 | break; 76 | } 77 | } 78 | 79 | 80 | Given(/^I have an authorized key$/,function(callback){ 81 | headers["apikey"] = config.API_KEY 82 | callback(); 83 | }) 84 | 85 | 86 | When(/^I GET the service root$/, 87 | function (callback) { 88 | this.get('/v1', headers, callback) 89 | }) 90 | 91 | When(/^I GET the version$/, 92 | function (callback) { 93 | this.get("version/", headers, callback) 94 | }) 95 | 96 | When(/^I GET the employees list "([^"]*)"$/, 97 | function (invoiceId, callback) { 98 | var url = "v1/" ; 99 | this.get(url,headers, callback) 100 | }) 101 | 102 | When(/^I GET the employees detail "([^"]*)"$/, 103 | function (id, callback) { 104 | var url = "v1/" + id; 105 | this.get(url, headers, callback) 106 | }) 107 | 108 | 109 | //=== end user ===== 110 | When(/^I GET a non-existing resource$/, function (callback) { 111 | this.get('/does/not/exist',headers, callback) 112 | }) 113 | 114 | 115 | Then(/^the http status should be (\d+)$/, function (status, callback) { 116 | if (!assertResponse(this.lastResponse, callback)) { return } 117 | // deliberately using != here (no need to cast integer/string) 118 | /* jshint -W116 */ 119 | if (this.lastResponse.statusCode != status) { 120 | /* jshint +W116 */ 121 | callback('The last http response did not have the expected ' + 122 | 'status, expected ' + status + ' but got ' + 123 | this.lastResponse.statusCode) 124 | } else { 125 | callback() 126 | } 127 | }) 128 | Then(/^(?:the )?([\w_.$\[\]]+) should equal to null$/, function (key, callback) { 129 | if (!assertPropertyIsNotNull(this.lastResponse, key, callback)) { 130 | //callback() 131 | //return 132 | } 133 | callback() 134 | }); 135 | // Check if a certain property of the response is equal to something 136 | Then(/^(?:the )?([\w_.$\[\]]+) should equal "([^"]+)"$/, 137 | function (key, expectedValue, callback) { 138 | if (!assertPropertyIs(this.lastResponse, key, expectedValue, callback)) { 139 | //return 140 | } 141 | callback() 142 | }) 143 | Then(/^(?:the )?([\w_.$\[\]]+) should not equal to null$/, function (key,callback) { 144 | if (!assertPropertyIsNotNull(this.lastResponse, key, callback)) { 145 | //callback() 146 | //return 147 | } 148 | callback() 149 | }); 150 | // Check if a substring is contained in a certain property of the response 151 | Then(/^I should see "([^"]+)" in the (\w+)$/, function (callback) 152 | { 153 | callback() 154 | }) 155 | 156 | function assertResponse(lastResponse, callback) { 157 | if (!lastResponse) { 158 | callback('No request has been made until now.') 159 | return false 160 | } 161 | return true 162 | } 163 | 164 | function assertBody(lastResponse, callback) { 165 | if (!assertResponse(lastResponse, callback)) { return false } 166 | if (!lastResponse.body) { 167 | callback('The response to the last request had no body.') 168 | return null 169 | } 170 | return lastResponse.body 171 | } 172 | 173 | function assertValidJson(lastResponse, callback) { 174 | var body = assertBody(lastResponse, callback) 175 | if (!body) { 176 | return null 177 | } 178 | try { 179 | return JSON.parse(body) 180 | } catch (e) { 181 | callback( 182 | 'The body of the last response was not valid JSON.') 183 | return null 184 | } 185 | } 186 | 187 | function assertPropertyExists(lastResponse, key, expectedValue, 188 | callback) { 189 | var object = assertValidJson(lastResponse, callback) 190 | if (!object) { return null } 191 | var property 192 | if (key.indexOf('$.') !== 0 && key.indexOf('$[') !== 0) { 193 | // normal property 194 | property = object[key] 195 | } else { 196 | // JSONPath expression 197 | var matches = jsonPath(object, key) 198 | if (matches.length === 0) { 199 | // no match 200 | callback('The last response did not have the property: ' + 201 | key + '\nExpected it to be\n' + expectedValue) 202 | return null 203 | } else if (matches.length > 1) { 204 | // ambigious match 205 | callback('JSONPath expression ' + key + ' returned more than ' + 206 | 'one match in object:\n' + JSON.stringify(object)) 207 | return null 208 | } else { 209 | // exactly one match, good 210 | property = matches[0] 211 | } 212 | } 213 | if (property == null) { 214 | callback('The last response did not have the property ' + 215 | key + '\nExpected it to be\n' + expectedValue) 216 | return null 217 | } 218 | return property 219 | } 220 | 221 | function assertPropertyIs(lastResponse, key, expectedValue, callback) { 222 | 223 | var value = assertPropertyExists(lastResponse, key, expectedValue, callback) 224 | if (value == null) { return false; } // success false 225 | if (value.toString() !== expectedValue) { 226 | 227 | callback('The last response did not have the expected content in ' + 228 | 'property ' + key + '. ' + 'Got:\n\n' + value + '\n\nExpected:\n\n' + 229 | expectedValue) 230 | 231 | return false 232 | } 233 | return true 234 | } 235 | function assertPropertyIsNotNull(lastResponse, key, callback) { 236 | 237 | var value = assertPropertyExists(lastResponse, key, null, callback) 238 | if (value && value.length) { 239 | return true 240 | } 241 | return false 242 | } 243 | 244 | function assertPropertyContains(lastResponse, key, expectedValue, callback) { 245 | var value = assertPropertyExists(lastResponse, key, expectedValue, callback) 246 | if (!value) { return false } 247 | if (value.indexOf(expectedValue) === -1) { 248 | callback('The last response did not have the expected content in ' + 249 | 'property ' + key + '. ' + 250 | 'Got:\n\n' + value + '\n\nExpected it to contain:\n\n' + expectedValue) 251 | return false 252 | } 253 | return true 254 | } 255 | }); -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/features/support/world.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var request = require('request') 4 | 5 | var config = require('../step_definitions/config') 6 | 7 | var World = function World(callback) { 8 | var self = this 9 | this.defaultHeaders = {'User-Agent': 'request','Content-Type': 'application/json; charset=utf-8'}; 10 | this.lastResponse = null 11 | 12 | this.get = function(path, headers, callback) { 13 | var uri = this.uri(path) 14 | console.log(uri); 15 | request.get({url: uri, headers: this.addDefaultHeaders(headers)}, 16 | function (error, response) { 17 | console.log("response "+ response); 18 | if (error || !('' + response.statusCode).match(/^2\d\d$/)) { 19 | callback('Error on GET request to ' + uri + 20 | ': ' + (error!=null?error.message: response.body)) 21 | return 22 | } 23 | 24 | self.lastResponse = response 25 | callback() 26 | }) 27 | } 28 | 29 | this.post = function(path, requestBody, headers, callback) { 30 | var uri = this.uri(path) 31 | //console.log(uri); 32 | request({url: uri, body: requestBody, method: 'POST',headers: this.addDefaultHeaders(headers)} 33 | ,function(error, response) { 34 | //console.log("response "+ response.body); 35 | if (error || !('' + response.statusCode).match(/^2\d\d$/)) { 36 | return callback('Error on POST request to ' + uri + ': ' + 37 | (error!=null?error.message: response.body)) 38 | } 39 | self.lastResponse = response 40 | callback(null, self.lastResponse.headers.location) 41 | }) 42 | } 43 | 44 | this.put = function(path, requestBody, headers, callback) { 45 | var uri = this.uri(path) 46 | request({url: uri, body: requestBody, method: 'PUT', 47 | headers: this.addDefaultHeaders(headers)}, 48 | function(error, response) { 49 | if (error) { 50 | return callback('Error on PUT request to ' + uri + ': ' + 51 | error.message) 52 | } 53 | self.lastResponse = response 54 | callback(null, self.lastResponse.headers.locations) 55 | }) 56 | } 57 | 58 | this.delete = function(path, headers, callback) { 59 | var uri = this.uri(path) 60 | request({ url: uri, method: 'DELETE', 61 | headers: this.addDefaultHeaders(headers)}, 62 | function(error, response) { 63 | if (error) { 64 | return callback('Error on DELETE request to ' + uri + ': ' + 65 | error.message) 66 | } 67 | self.lastResponse = response 68 | callback() 69 | }) 70 | } 71 | 72 | this.options = function(path, headers, callback) { 73 | var uri = this.uri(path) 74 | request({'uri': uri, method: 'OPTIONS', 75 | headers: this.addDefaultHeaders(headers) 76 | }, 77 | function(error, response) { 78 | if (error) { 79 | return callback('Error on OPTIONS request to ' + uri + 80 | ': ' + error.message) 81 | } 82 | self.lastResponse = response 83 | callback() 84 | }) 85 | } 86 | 87 | this.rootPath = function() { 88 | return '/' 89 | } 90 | 91 | this.uri = function(path) { 92 | return config.BASE_URL + path 93 | } 94 | 95 | this.addDefaultHeaders = function(reqHeaders){ 96 | reqHeaders= reqHeaders || []; 97 | for(var h in this.defaultHeaders){ 98 | reqHeaders[h] = this.defaultHeaders[h] 99 | } 100 | return reqHeaders; 101 | } 102 | } 103 | 104 | exports.World = World 105 | -------------------------------------------------------------------------------- /src/Core.Services.Tests.Automation/tests/report: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keyword": "Feature", 4 | "line": 1, 5 | "name": "Invoice's automation tests", 6 | "tags": [], 7 | "uri": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\service.feature", 8 | "elements": [ 9 | { 10 | "keyword": "Scenario", 11 | "line": 3, 12 | "name": "Get the version", 13 | "tags": [], 14 | "id": "invoice's-automation-tests;get-the-version", 15 | "steps": [ 16 | { 17 | "arguments": [], 18 | "keyword": "Before", 19 | "result": { 20 | "status": "passed", 21 | "duration": 1 22 | }, 23 | "hidden": true, 24 | "match": { 25 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\hooks.js:24" 26 | } 27 | }, 28 | { 29 | "arguments": [], 30 | "keyword": "Before", 31 | "result": { 32 | "status": "passed", 33 | "duration": 1 34 | }, 35 | "hidden": true, 36 | "match": { 37 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:17" 38 | } 39 | }, 40 | { 41 | "arguments": [], 42 | "keyword": "When ", 43 | "name": "I GET the version", 44 | "result": { 45 | "status": "failed", 46 | "duration": 1021, 47 | "error_message": "Error: Error on GET request to http://localhost:52973/serviceversion/: connect ECONNREFUSED 127.0.0.1:52973\n at Function. (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\cucumber\\lib\\user_code_runner.js:105:21)\n at throw (native)\n at tryCatcher (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\util.js:16:23)\n at PromiseSpawn._promiseRejected (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\generators.js:107:10)\n at Promise._settlePromise (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:576:26)\n at Promise._settlePromise0 (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:614:10)\n at Promise._settlePromises (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:689:18)\n at Async._drainQueue (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:133:16)\n at Async._drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:143:10)\n at Immediate.Async.drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:17:14)\n at runCallback (timers.js:672:20)\n at tryOnImmediate (timers.js:645:5)\n at processImmediate [as _immediateCallback] (timers.js:617:5)" 48 | }, 49 | "line": 4, 50 | "match": { 51 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:91" 52 | } 53 | }, 54 | { 55 | "arguments": [], 56 | "keyword": "Then ", 57 | "name": "the http status should be 200", 58 | "result": { 59 | "status": "skipped" 60 | }, 61 | "line": 5, 62 | "match": { 63 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:115" 64 | } 65 | } 66 | ] 67 | }, 68 | { 69 | "keyword": "Scenario", 70 | "line": 7, 71 | "name": "Get the employees detail", 72 | "tags": [], 73 | "id": "invoice's-automation-tests;get-the-employees-detail", 74 | "steps": [ 75 | { 76 | "arguments": [], 77 | "keyword": "Before", 78 | "result": { 79 | "status": "passed", 80 | "duration": 0 81 | }, 82 | "hidden": true, 83 | "match": { 84 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\hooks.js:24" 85 | } 86 | }, 87 | { 88 | "arguments": [], 89 | "keyword": "Before", 90 | "result": { 91 | "status": "passed", 92 | "duration": 0 93 | }, 94 | "hidden": true, 95 | "match": { 96 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:17" 97 | } 98 | }, 99 | { 100 | "arguments": [], 101 | "keyword": "Given ", 102 | "name": "I have an authorized key", 103 | "result": { 104 | "status": "passed", 105 | "duration": 1 106 | }, 107 | "line": 8, 108 | "match": { 109 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:80" 110 | } 111 | }, 112 | { 113 | "arguments": [], 114 | "keyword": "And ", 115 | "name": "path parameter id = \"102\"", 116 | "result": { 117 | "status": "passed", 118 | "duration": 1 119 | }, 120 | "line": 9, 121 | "match": { 122 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 123 | } 124 | }, 125 | { 126 | "arguments": [], 127 | "keyword": "When ", 128 | "name": "I send a \"GET\" request on \"v1/{id}\"", 129 | "result": { 130 | "status": "failed", 131 | "duration": 1008, 132 | "error_message": "Error: Error on GET request to http://localhost:52973/servicev1/102: connect ECONNREFUSED 127.0.0.1:52973\n at Function. (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\cucumber\\lib\\user_code_runner.js:105:21)\n at throw (native)\n at tryCatcher (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\util.js:16:23)\n at PromiseSpawn._promiseRejected (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\generators.js:107:10)\n at Promise._settlePromise (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:576:26)\n at Promise._settlePromise0 (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:614:10)\n at Promise._settlePromises (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:689:18)\n at Async._drainQueue (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:133:16)\n at Async._drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:143:10)\n at Immediate.Async.drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:17:14)\n at runCallback (timers.js:672:20)\n at tryOnImmediate (timers.js:645:5)\n at processImmediate [as _immediateCallback] (timers.js:617:5)" 133 | }, 134 | "line": 10, 135 | "match": { 136 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:34" 137 | } 138 | }, 139 | { 140 | "arguments": [], 141 | "keyword": "Then ", 142 | "name": "the http status should be 200", 143 | "result": { 144 | "status": "skipped" 145 | }, 146 | "line": 11, 147 | "match": { 148 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:115" 149 | } 150 | }, 151 | { 152 | "arguments": [], 153 | "keyword": "And ", 154 | "name": "the result should not equal to null", 155 | "result": { 156 | "status": "skipped" 157 | }, 158 | "line": 12, 159 | "match": { 160 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:143" 161 | } 162 | } 163 | ] 164 | }, 165 | { 166 | "keyword": "Scenario", 167 | "line": 14, 168 | "name": "Get the employees list", 169 | "tags": [], 170 | "id": "invoice's-automation-tests;get-the-employees-list", 171 | "steps": [ 172 | { 173 | "arguments": [], 174 | "keyword": "Before", 175 | "result": { 176 | "status": "passed", 177 | "duration": 0 178 | }, 179 | "hidden": true, 180 | "match": { 181 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\hooks.js:24" 182 | } 183 | }, 184 | { 185 | "arguments": [], 186 | "keyword": "Before", 187 | "result": { 188 | "status": "passed", 189 | "duration": 1 190 | }, 191 | "hidden": true, 192 | "match": { 193 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:17" 194 | } 195 | }, 196 | { 197 | "arguments": [], 198 | "keyword": "Given ", 199 | "name": "I have an authorized key", 200 | "result": { 201 | "status": "passed", 202 | "duration": 1 203 | }, 204 | "line": 15, 205 | "match": { 206 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:80" 207 | } 208 | }, 209 | { 210 | "arguments": [], 211 | "keyword": "When ", 212 | "name": "I send a \"GET\" request on \"v1/\"", 213 | "result": { 214 | "status": "failed", 215 | "duration": 1006, 216 | "error_message": "Error: Error on GET request to http://localhost:52973/servicev1/: connect ECONNREFUSED 127.0.0.1:52973\n at Function. (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\cucumber\\lib\\user_code_runner.js:105:21)\n at throw (native)\n at tryCatcher (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\util.js:16:23)\n at PromiseSpawn._promiseRejected (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\generators.js:107:10)\n at Promise._settlePromise (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:576:26)\n at Promise._settlePromise0 (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:614:10)\n at Promise._settlePromises (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:689:18)\n at Async._drainQueue (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:133:16)\n at Async._drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:143:10)\n at Immediate.Async.drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:17:14)\n at runCallback (timers.js:672:20)\n at tryOnImmediate (timers.js:645:5)\n at processImmediate [as _immediateCallback] (timers.js:617:5)" 217 | }, 218 | "line": 16, 219 | "match": { 220 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:34" 221 | } 222 | }, 223 | { 224 | "arguments": [], 225 | "keyword": "Then ", 226 | "name": "the http status should be 200", 227 | "result": { 228 | "status": "skipped" 229 | }, 230 | "line": 17, 231 | "match": { 232 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:115" 233 | } 234 | }, 235 | { 236 | "arguments": [], 237 | "keyword": "And ", 238 | "name": "the result should equal to null", 239 | "result": { 240 | "status": "skipped" 241 | }, 242 | "line": 18, 243 | "match": { 244 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:128" 245 | } 246 | } 247 | ] 248 | }, 249 | { 250 | "keyword": "Scenario", 251 | "line": 20, 252 | "name": "Submit Add new employee", 253 | "tags": [], 254 | "id": "invoice's-automation-tests;submit-add-new-employee", 255 | "steps": [ 256 | { 257 | "arguments": [], 258 | "keyword": "Before", 259 | "result": { 260 | "status": "passed", 261 | "duration": 1 262 | }, 263 | "hidden": true, 264 | "match": { 265 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\hooks.js:24" 266 | } 267 | }, 268 | { 269 | "arguments": [], 270 | "keyword": "Before", 271 | "result": { 272 | "status": "passed", 273 | "duration": 0 274 | }, 275 | "hidden": true, 276 | "match": { 277 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:17" 278 | } 279 | }, 280 | { 281 | "arguments": [], 282 | "keyword": "Given ", 283 | "name": "I have an authorized key", 284 | "result": { 285 | "status": "passed", 286 | "duration": 0 287 | }, 288 | "line": 21, 289 | "match": { 290 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:80" 291 | } 292 | }, 293 | { 294 | "arguments": [], 295 | "keyword": "And ", 296 | "name": "path parameter Name = \"AutomationUser\"", 297 | "result": { 298 | "status": "passed", 299 | "duration": 0 300 | }, 301 | "line": 22, 302 | "match": { 303 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 304 | } 305 | }, 306 | { 307 | "arguments": [], 308 | "keyword": "And ", 309 | "name": "body parameter Address = \"Delhi\"", 310 | "result": { 311 | "status": "passed", 312 | "duration": 0 313 | }, 314 | "line": 23, 315 | "match": { 316 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 317 | } 318 | }, 319 | { 320 | "arguments": [], 321 | "keyword": "And ", 322 | "name": "body parameter DepartmentId = \"1002\"", 323 | "result": { 324 | "status": "passed", 325 | "duration": 0 326 | }, 327 | "line": 24, 328 | "match": { 329 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 330 | } 331 | }, 332 | { 333 | "arguments": [], 334 | "keyword": "And ", 335 | "name": "body parameter Salary = \"3500\"", 336 | "result": { 337 | "status": "passed", 338 | "duration": 0 339 | }, 340 | "line": 25, 341 | "match": { 342 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 343 | } 344 | }, 345 | { 346 | "arguments": [], 347 | "keyword": "When ", 348 | "name": "I send a \"POST\" request on \"v1/\"", 349 | "result": { 350 | "status": "failed", 351 | "duration": 1007, 352 | "error_message": "Error: Error on POST request to http://localhost:52973/servicev1/: connect ECONNREFUSED 127.0.0.1:52973\n at Function. (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\cucumber\\lib\\user_code_runner.js:105:21)\n at throw (native)\n at tryCatcher (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\util.js:16:23)\n at PromiseSpawn._promiseRejected (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\generators.js:107:10)\n at Promise._settlePromise (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:576:26)\n at Promise._settlePromise0 (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:614:10)\n at Promise._settlePromises (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:689:18)\n at Async._drainQueue (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:133:16)\n at Async._drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:143:10)\n at Immediate.Async.drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:17:14)\n at runCallback (timers.js:672:20)\n at tryOnImmediate (timers.js:645:5)\n at processImmediate [as _immediateCallback] (timers.js:617:5)" 353 | }, 354 | "line": 26, 355 | "match": { 356 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:34" 357 | } 358 | }, 359 | { 360 | "arguments": [], 361 | "keyword": "Then ", 362 | "name": "the http status should be 200", 363 | "result": { 364 | "status": "skipped" 365 | }, 366 | "line": 27, 367 | "match": { 368 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:115" 369 | } 370 | }, 371 | { 372 | "arguments": [], 373 | "keyword": "And ", 374 | "name": "the success should equal \"true\"", 375 | "result": { 376 | "status": "skipped" 377 | }, 378 | "line": 28, 379 | "match": { 380 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:136" 381 | } 382 | } 383 | ] 384 | }, 385 | { 386 | "keyword": "Scenario", 387 | "line": 30, 388 | "name": "Submit delete new employee", 389 | "tags": [], 390 | "id": "invoice's-automation-tests;submit-delete-new-employee", 391 | "steps": [ 392 | { 393 | "arguments": [], 394 | "keyword": "Before", 395 | "result": { 396 | "status": "passed", 397 | "duration": 0 398 | }, 399 | "hidden": true, 400 | "match": { 401 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\hooks.js:24" 402 | } 403 | }, 404 | { 405 | "arguments": [], 406 | "keyword": "Before", 407 | "result": { 408 | "status": "passed", 409 | "duration": 1 410 | }, 411 | "hidden": true, 412 | "match": { 413 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:17" 414 | } 415 | }, 416 | { 417 | "arguments": [], 418 | "keyword": "Given ", 419 | "name": "I have an authorized key", 420 | "result": { 421 | "status": "passed", 422 | "duration": 1 423 | }, 424 | "line": 31, 425 | "match": { 426 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:80" 427 | } 428 | }, 429 | { 430 | "arguments": [], 431 | "keyword": "And ", 432 | "name": "path parameter id = \"101\"", 433 | "result": { 434 | "status": "passed", 435 | "duration": 1 436 | }, 437 | "line": 32, 438 | "match": { 439 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:26" 440 | } 441 | }, 442 | { 443 | "arguments": [], 444 | "keyword": "When ", 445 | "name": "I send a \"DELETE\" request on \"v1/{id}\"", 446 | "result": { 447 | "status": "failed", 448 | "duration": 1009, 449 | "error_message": "Error: Error on DELETE request to http://localhost:52973/servicev1/101: connect ECONNREFUSED 127.0.0.1:52973\n at Function. (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\cucumber\\lib\\user_code_runner.js:105:21)\n at throw (native)\n at tryCatcher (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\util.js:16:23)\n at PromiseSpawn._promiseRejected (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\generators.js:107:10)\n at Promise._settlePromise (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:576:26)\n at Promise._settlePromise0 (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:614:10)\n at Promise._settlePromises (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\promise.js:689:18)\n at Async._drainQueue (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:133:16)\n at Async._drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:143:10)\n at Immediate.Async.drainQueues (D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\node_modules\\bluebird\\js\\release\\async.js:17:14)\n at runCallback (timers.js:672:20)\n at tryOnImmediate (timers.js:645:5)\n at processImmediate [as _immediateCallback] (timers.js:617:5)" 450 | }, 451 | "line": 33, 452 | "match": { 453 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:34" 454 | } 455 | }, 456 | { 457 | "arguments": [], 458 | "keyword": "Then ", 459 | "name": "the http status should be 200", 460 | "result": { 461 | "status": "skipped" 462 | }, 463 | "line": 34, 464 | "match": { 465 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:115" 466 | } 467 | }, 468 | { 469 | "arguments": [], 470 | "keyword": "And ", 471 | "name": "the success should equal \"true\"", 472 | "result": { 473 | "status": "skipped" 474 | }, 475 | "line": 35, 476 | "match": { 477 | "location": "D:\\Rakhi\\TPG\\CoreServiceTemplate\\Core.Services\\Core.Services.Tests.Automation\\tests\\features\\step_definitions\\stepDefinition.js:136" 478 | } 479 | } 480 | ] 481 | } 482 | ], 483 | "id": "invoice's-automation-tests" 484 | } 485 | ] -------------------------------------------------------------------------------- /src/Core.Services.Tests.Unit/Core.Services.Tests.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Core.Services.Tests.Unit/ServiceControllerTests.cs: -------------------------------------------------------------------------------- 1 | using Castle.Core.Logging; 2 | using Core.Services.Areas.V1.Controlllers; 3 | using Core.Services.Areas.V1.Models.Requests; 4 | using Core.Services.Areas.V1.Models.Responses; 5 | using Core.Services.Configurations; 6 | using Core.Services.Entities; 7 | using Core.Services.Repositories.Database; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.VisualStudio.TestTools.UnitTesting; 10 | using Moq; 11 | using System.Collections.Generic; 12 | using System.ComponentModel.DataAnnotations; 13 | using System.Linq; 14 | 15 | namespace Core.Services.Tests.Unit 16 | { 17 | [TestClass] 18 | public class ServiceControllerTests 19 | { 20 | #region Initialize 21 | Mock mockAppSettings = null; 22 | Mock mockDatabaseRepository = null; 23 | 24 | 25 | [TestInitialize] 26 | public void Initialize() 27 | { 28 | mockAppSettings = new Mock(); 29 | mockDatabaseRepository = new Mock(); 30 | mockDatabaseRepository.Setup(ee=>ee.AddEmployee(Moq.It.IsAny(), Moq.It.IsAny(), Moq.It.IsAny(), Moq.It.IsAny())).Returns(1); 31 | mockDatabaseRepository.Setup(ee => ee.RemoveEmployee(Moq.It.IsAny())).Returns(1); 32 | mockDatabaseRepository.Setup(ee => ee.GetEmployeeDetailById(Moq.It.IsAny())).Returns(new Entities.EmployeeDetail() { Name="test",Address="gg",Salary=333,DeptName="HR"}); 33 | mockDatabaseRepository.Setup(ee => ee.GetEmployeesList()).Returns(new List() { new Employee() {EmployeeId=101, Name = "test", DeptName = "HR" } }); 34 | } 35 | #endregion 36 | 37 | #region Validation Failed StatusCode: 400 38 | [TestMethod] 39 | public void AddEmployee_400_Name_Missing() 40 | { 41 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 42 | var request = new AddEmployeeRequest() { }; 43 | var validationResults = ValidateRequest(request); 44 | foreach (var validationResult in validationResults) 45 | { 46 | controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage); 47 | } 48 | var response = controller.AddEmployee(request) as ObjectResult; 49 | var result = response.Value as AddEmployeeResponse; 50 | Assert.AreEqual(result.Success, false); 51 | Assert.IsTrue(result.ErrorResponse.Message.Contains("Name")); 52 | Assert.AreEqual(response.StatusCode, 400); 53 | } 54 | [TestMethod] 55 | public void AddEmployee_400_Address_Missing() 56 | { 57 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 58 | var request = new AddEmployeeRequest() {Name="Unit Testing",DepartmentId=1002,Salary=3200 }; 59 | var validationResults = ValidateRequest(request); 60 | foreach (var validationResult in validationResults) 61 | { 62 | controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage); 63 | } 64 | var response = controller.AddEmployee(request) as ObjectResult; 65 | var result = response.Value as AddEmployeeResponse; 66 | Assert.AreEqual(result.Success, false); 67 | Assert.IsTrue(result.ErrorResponse.Message.Contains("Address")); 68 | Assert.AreEqual(response.StatusCode, 400); 69 | } 70 | [TestMethod] 71 | public void AddEmployee_400_Salary_Missing() 72 | { 73 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 74 | var request = new AddEmployeeRequest() { Name = "Unit Testing", Address = "TestAddress", DepartmentId = 1002 }; 75 | var validationResults = ValidateRequest(request); 76 | foreach (var validationResult in validationResults) 77 | { 78 | controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage); 79 | } 80 | var response = controller.AddEmployee(request) as ObjectResult; 81 | var result = response.Value as AddEmployeeResponse; 82 | Assert.AreEqual(result.Success, false); 83 | Assert.IsTrue(result.ErrorResponse.Message.Contains("Salary")); 84 | Assert.AreEqual(response.StatusCode, 400); 85 | } 86 | [TestMethod] 87 | public void AddEmployee_200_OK() 88 | { 89 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 90 | var request = new AddEmployeeRequest() { Name = "Unit Testing", Address = "TestAddress", DepartmentId = 1002, Salary = 3200 }; 91 | var validationResults = ValidateRequest(request); 92 | foreach (var validationResult in validationResults) 93 | { 94 | controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage); 95 | } 96 | var response = controller.AddEmployee(request) as ObjectResult; 97 | var result = response.Value as AddEmployeeResponse; 98 | Assert.AreEqual(result.Success, true); 99 | Assert.AreEqual(response.StatusCode, 200); 100 | } 101 | [TestMethod] 102 | public void GetEmployeeById_200_OK() 103 | { 104 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 105 | var response = controller.GetEmployeeById(101) as ObjectResult; 106 | var result = response.Value as GetEmployeeResponse; 107 | Assert.AreEqual(result.Success, true); 108 | Assert.AreEqual(response.StatusCode, 200); 109 | } 110 | [TestMethod] 111 | public void GetEmployeeList_200_OK() 112 | { 113 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 114 | var response = controller.GetEmployeeList() as ObjectResult; 115 | var result = response.Value as GetEmployeesListResponse; 116 | Assert.AreEqual(result.Success, true); 117 | Assert.AreEqual(response.StatusCode, 200); 118 | } 119 | [TestMethod] 120 | public void RemoveEmployee_200_OK() 121 | { 122 | var controller = new ServiceController(mockAppSettings.Object, mockDatabaseRepository.Object); 123 | var response = controller.RemoveEmployee(102) as ObjectResult; 124 | var result = response.Value as RemoveEmployeeResponse; 125 | Assert.AreEqual(result.Success, true); 126 | Assert.AreEqual(response.StatusCode, 200); 127 | } 128 | #endregion 129 | #region private methods 130 | private List ValidateRequest(AddEmployeeRequest request) 131 | { 132 | var validationContext = new ValidationContext(request, null, null); 133 | var validationResults = new List(); 134 | Validator.TryValidateObject(request, validationContext, validationResults, true); 135 | return validationResults; 136 | } 137 | #endregion 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Core.Services.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2005 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Services", "Core.Services\Core.Services.csproj", "{8A93C7F9-BCDA-4C30-8D3B-D5AED9F281EF}" 7 | EndProject 8 | Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FD57D91B-39F1-41D1-BB1B-34BF6BD90075}" 9 | EndProject 10 | Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "Core.Services.Tests.Automation", "Core.Services.Tests.Automation\Core.Services.Tests.Automation.njsproj", "{F37735B1-251A-4CE6-A238-68CE76A1A149}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Services.Tests.Unit", "Core.Services.Tests.Unit\Core.Services.Tests.Unit.csproj", "{6AC225C7-9743-4B04-80D4-58BE9150ACD0}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {8A93C7F9-BCDA-4C30-8D3B-D5AED9F281EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8A93C7F9-BCDA-4C30-8D3B-D5AED9F281EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8A93C7F9-BCDA-4C30-8D3B-D5AED9F281EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {8A93C7F9-BCDA-4C30-8D3B-D5AED9F281EF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {FD57D91B-39F1-41D1-BB1B-34BF6BD90075}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {FD57D91B-39F1-41D1-BB1B-34BF6BD90075}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {FD57D91B-39F1-41D1-BB1B-34BF6BD90075}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {FD57D91B-39F1-41D1-BB1B-34BF6BD90075}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {F37735B1-251A-4CE6-A238-68CE76A1A149}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {F37735B1-251A-4CE6-A238-68CE76A1A149}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {F37735B1-251A-4CE6-A238-68CE76A1A149}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {F37735B1-251A-4CE6-A238-68CE76A1A149}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {6AC225C7-9743-4B04-80D4-58BE9150ACD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {6AC225C7-9743-4B04-80D4-58BE9150ACD0}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {6AC225C7-9743-4B04-80D4-58BE9150ACD0}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {6AC225C7-9743-4B04-80D4-58BE9150ACD0}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {EF05920F-E2A8-4F0A-8F58-3E1DEEE0140E} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /src/Core.Services/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !obj/Docker/publish/* 3 | !obj/Docker/empty/ 4 | !newrelic 5 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Controlllers/AddEmployee.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Areas.V1.Models.Requests; 2 | using Core.Services.Areas.V1.Models.Responses; 3 | using Core.Services.Entities; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Linq; 6 | 7 | namespace Core.Services.Areas.V1.Controlllers 8 | { 9 | public partial class ServiceController : Controller 10 | { 11 | [Route("")] 12 | [HttpPost] 13 | public IActionResult AddEmployee([FromBody]AddEmployeeRequest request) 14 | { 15 | var response =new AddEmployeeResponse(); 16 | 17 | if (!ModelState.IsValid) 18 | { 19 | var errors = ModelState.Where(e => e.Value.Errors.Count > 0).Select(ee => ee.Value.Errors.First().ErrorMessage); 20 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse(errors.FirstOrDefault(), ErrorsType.ValidationError.ToString(), ErrorMessageType.Validation.ToString()); 21 | return BadRequest(response); 22 | } 23 | 24 | var result = _dbRepository.AddEmployee(request.Name,request.Address,request.Salary,request.DepartmentId); 25 | if (result > 0) 26 | { 27 | response.Result = true; 28 | response.Success = true; 29 | } 30 | else 31 | { 32 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("Some error occured in adding employee..", ErrorsType.DatabaseError.ToString(), ErrorMessageType.Error.ToString()); 33 | } 34 | return Ok(response); 35 | 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Controlllers/GetEmployeeById.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Areas.V1.Models.Responses; 2 | using Core.Services.Entities; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Linq; 5 | using System.Net; 6 | 7 | namespace Core.Services.Areas.V1.Controlllers 8 | { 9 | public partial class ServiceController : Controller 10 | { 11 | [Route("{id:int}")] 12 | [HttpGet] 13 | public IActionResult GetEmployeeById(int id) 14 | { 15 | var response =new GetEmployeeResponse(); 16 | 17 | if(id<=0) 18 | { 19 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("Invalid Id..", ErrorsType.ValidationError.ToString(), ErrorMessageType.Validation.ToString()); 20 | return BadRequest(response); 21 | } 22 | 23 | var result = _dbRepository.GetEmployeeDetailById(id); 24 | if (result != null) 25 | { 26 | response.Result = result; 27 | response.Success = true; 28 | return Ok(response); 29 | } 30 | else 31 | { 32 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("No Resource found..", ErrorsType.ResourceNotFoundError.ToString(), ErrorMessageType.Error.ToString()); 33 | return NotFound(response); 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Controlllers/GetEmployeeList.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Areas.V1.Models.Responses; 2 | using Core.Services.Entities; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Linq; 5 | 6 | namespace Core.Services.Areas.V1.Controlllers 7 | { 8 | public partial class ServiceController : Controller 9 | { 10 | [Route("")] 11 | [HttpGet] 12 | public IActionResult GetEmployeeList() 13 | { 14 | var response =new GetEmployeesListResponse(); 15 | 16 | var result= _dbRepository.GetEmployeesList(); 17 | if (result != null && result.Any()) 18 | { 19 | response.Result = result; 20 | response.Success = true; 21 | } 22 | else 23 | { 24 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("No employee record found..",ErrorsType.NoRecordFound.ToString() , ErrorMessageType.Validation.ToString()); 25 | } 26 | 27 | return Ok(response); 28 | 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Controlllers/RemoveEmployee.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Areas.V1.Models.Responses; 2 | using Core.Services.Entities; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Linq; 5 | 6 | namespace Core.Services.Areas.V1.Controlllers 7 | { 8 | public partial class ServiceController : Controller 9 | { 10 | [Route("{id:int}")] 11 | [HttpDelete] 12 | public IActionResult RemoveEmployee(int id) 13 | { 14 | var response =new RemoveEmployeeResponse(); 15 | 16 | if(id<=0) 17 | { 18 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("Invalid Id..", ErrorsType.ValidationError.ToString(), ErrorMessageType.Validation.ToString()); 19 | return BadRequest(response); 20 | } 21 | if (!ModelState.IsValid) 22 | { 23 | var errors = ModelState.Where(e => e.Value.Errors.Count > 0).Select(ee => ee.Value.Errors.First().ErrorMessage); 24 | 25 | return BadRequest(errors); 26 | } 27 | 28 | var result = _dbRepository.RemoveEmployee(id); 29 | if (result > 0) 30 | { 31 | response.Result = true; 32 | response.Success = true; 33 | } 34 | else 35 | { 36 | response.ErrorResponse = Helpers.Helper.ConvertToErrorResponse("Some error occured in removing employee info..", ErrorsType.DatabaseError.ToString(), ErrorMessageType.Error.ToString()); 37 | } 38 | return Ok(response); 39 | 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Controlllers/_ServiceController.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Configurations; 2 | using Core.Services.Filters; 3 | using Core.Services.Repositories.Database; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace Core.Services.Areas.V1.Controlllers 7 | { 8 | [Route("/service/v1")] 9 | [ServiceFilter(typeof(CustomAuthorize))] 10 | public partial class ServiceController : Controller 11 | { 12 | public const int REFUND_NOTE_TYPE_ID = 24; 13 | public const int TAX_EXEMPT_ID = 2; 14 | private readonly IAppSettings _apiSettings; 15 | private readonly IDatabaseRepository _dbRepository; 16 | 17 | public ServiceController(IAppSettings apiSettings, IDatabaseRepository dbRepository) 18 | { 19 | _apiSettings = apiSettings; 20 | _dbRepository = dbRepository; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Models/Requests/AddEmployeeRequest.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Core.Services.Areas.V1.Models.Requests 5 | { 6 | public class AddEmployeeRequest 7 | { 8 | [Required(AllowEmptyStrings = false,ErrorMessage ="Name is required..")] 9 | public string Name { get; set; } 10 | [Required(AllowEmptyStrings = false, ErrorMessage = "Address is required..")] 11 | public string Address { get; set; } 12 | [Range(1000,1100, ErrorMessage = "DepartmentId is required..")] 13 | public int DepartmentId { get; set; } 14 | [Range(1000,200000, ErrorMessage = "Salary is required..")] 15 | public int Salary { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Models/Responses/AddEmployeeResponse.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | 3 | namespace Core.Services.Areas.V1.Models.Responses 4 | { 5 | public class AddEmployeeResponse 6 | { 7 | public bool Success { get; set; } 8 | public bool Result { get; set; } 9 | public ResponseMessage ErrorResponse { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Models/Responses/GetEmployeeResponse.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | 3 | namespace Core.Services.Areas.V1.Models.Responses 4 | { 5 | public class GetEmployeeResponse 6 | { 7 | public bool Success { get; set; } 8 | public EmployeeDetail Result { get; set; } 9 | public ResponseMessage ErrorResponse { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Models/Responses/GetEmployeesListResponse.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | using System.Collections.Generic; 3 | 4 | namespace Core.Services.Areas.V1.Models.Responses 5 | { 6 | public class GetEmployeesListResponse 7 | { 8 | public bool Success { get; set; } 9 | public List Result { get; set; } 10 | public ResponseMessage ErrorResponse { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Core.Services/Areas/V1/Models/Responses/RemoveEmployeeResponse.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | 3 | namespace Core.Services.Areas.V1.Models.Responses 4 | { 5 | public class RemoveEmployeeResponse 6 | { 7 | public bool Success { get; set; } 8 | public bool Result { get; set; } 9 | public ResponseMessage ErrorResponse { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Core.Services/Configurations/AppSettings.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Core.Services.Configurations 3 | { 4 | public interface IAppSettings 5 | { 6 | string ApplicationName { get; set; } 7 | string ConnectionString { get; set; } 8 | bool IsProd { get; set; } 9 | string AuthKeyName { get; set; } 10 | string ApiKey { get; set; } 11 | } 12 | 13 | public class AppSettings : IAppSettings 14 | { 15 | public string ApplicationName { get; set; } 16 | public string ConnectionString { get; set; } 17 | public bool IsProd { get; set; } 18 | public string AuthKeyName { get; set; } 19 | public string ApiKey { get; set; } 20 | 21 | public AppSettings() 22 | { 23 | 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Core.Services/Controllers/VersionController.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Reflection; 4 | 5 | namespace Core.Services.Controllers 6 | { 7 | [Route("service")] 8 | public class VersionController : Controller 9 | { 10 | [Route("version")] 11 | [HttpGet] 12 | public IActionResult GetVersion() 13 | { 14 | var projectionsAssembly = Assembly.GetEntryAssembly(); 15 | var version = new VersionModel 16 | { 17 | Version = projectionsAssembly.GetCustomAttribute().InformationalVersion, 18 | Documents = new System.Collections.Generic.List 19 | { 20 | new VersionDocument 21 | { 22 | Status = VersionStatus.Live.ToString() 23 | } 24 | } 25 | }; 26 | return Ok(version); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Core.Services/Core.Services.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | ..\docker-compose.dcproj 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Core.Services/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/aspnetcore:2.0 2 | FROM microsoft/dotnet:2.0.0-sdk-2.0.2-stretch 3 | 4 | ENV CORECLR_ENABLE_PROFILING="1" \ 5 | CORECLR_PROFILER="{36032161-FFC0-4B61-B559-F6C5D41BAE5A}" \ 6 | CORECLR_NEWRELIC_HOME="/usr/local/newrelic-netcore20-agent" \ 7 | CORECLR_PROFILER_PATH="/usr/local/newrelic-netcore20-agent/libNewRelicProfiler.so" \ 8 | NEW_RELIC_LICENSE_KEY="3746113fec7f21e85135087aa6d47225369db05e" 9 | 10 | 11 | ARG source 12 | WORKDIR /app 13 | 14 | 15 | ARG NewRelic=./newrelic 16 | COPY $NewRelic ./newrelic 17 | 18 | RUN dpkg -i ./newrelic/newrelic-netcore20-agent*.deb 19 | 20 | ENV ASPNETCORE_URLS http://+:8601 21 | EXPOSE 8601 22 | COPY ${source:-obj/Docker/publish} . 23 | ENTRYPOINT ["dotnet", "Core.Services.dll"] 24 | -------------------------------------------------------------------------------- /src/Core.Services/Entities/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Core.Services.Entities 2 | { 3 | public enum ErrorMessageType 4 | { 5 | Validation, 6 | Error 7 | } 8 | public enum ErrorsType 9 | { 10 | ValidationError=100, 11 | ResourceNotFoundError = 102, 12 | NoRecordFound =101, 13 | DatabaseError=103, 14 | UnhandledError=104 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Core.Services/Entities/Employee.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Core.Services.Entities 3 | { 4 | public class Employee 5 | { 6 | public int EmployeeId { get; set; } 7 | public string Name { get; set; } 8 | public string DeptName { get; set; } 9 | } 10 | public class EmployeeDetail : Employee 11 | { 12 | public string Address { get; set; } 13 | public int Salary { get; set; } 14 | public bool IsActive { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Core.Services/Entities/ResponseMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Core.Services.Entities 2 | { 3 | public class ResponseMessage 4 | { 5 | public string MessageId { get; set; } 6 | public string Message { get; set; } 7 | public string FriendlyMessage { get; set; } 8 | public string MessageType { get; set; } 9 | public string StackTrace { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Core.Services/Entities/VersionDocument.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Core.Services.Entities 3 | { 4 | public class VersionDocument 5 | { 6 | public string Url { get; set; } 7 | public string Status { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Core.Services/Entities/VersionModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace Core.Services.Entities 5 | { 6 | public class VersionModel 7 | { 8 | public string Version { get; set; } 9 | [JsonProperty("Docs")] 10 | public List Documents { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Core.Services/Entities/VersionStatus.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Core.Services.Entities 3 | { 4 | public enum VersionStatus 5 | { 6 | Deprecated, 7 | Live, 8 | Preview 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Core.Services/Filters/CustomAuthorize.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | using System; 3 | using System.Linq; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Core.Services.Configurations; 6 | 7 | namespace Core.Services.Filters 8 | { 9 | [AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] 10 | public class CustomAuthorize : ActionFilterAttribute 11 | { 12 | private readonly IAppSettings _apiSettings; 13 | 14 | public CustomAuthorize(IAppSettings apiSettings) : base() 15 | { 16 | _apiSettings = apiSettings; 17 | } 18 | 19 | public CustomAuthorize() { } 20 | 21 | 22 | public override void OnActionExecuting(ActionExecutingContext context) 23 | { 24 | var headers = context.HttpContext.Request.Headers; 25 | bool isAuthorized = false; 26 | var authKeyName = _apiSettings.AuthKeyName; 27 | var allowedAuthKeys = _apiSettings.ApiKey; 28 | 29 | var allowedKeysList = allowedAuthKeys.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 30 | if (headers.Keys.Contains(authKeyName) && allowedKeysList.Any()) 31 | { 32 | var header = headers.FirstOrDefault(x => x.Key == authKeyName).Value.FirstOrDefault(); 33 | if (header != null) 34 | isAuthorized = Array.Exists(allowedKeysList, key => key.Equals(header)); 35 | } 36 | 37 | if (!isAuthorized) 38 | { 39 | context.Result = new ContentResult() 40 | { 41 | Content = "Authorization has been denied !!", 42 | ContentType = "text/plain", 43 | StatusCode = 401 44 | }; 45 | } 46 | } 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Core.Services/Filters/CustomExceptionFilter.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Configurations; 2 | using Core.Services.Entities; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Filters; 6 | using Microsoft.Extensions.Logging; 7 | using System; 8 | using System.IO; 9 | using System.Net; 10 | using System.Text; 11 | using System.Threading; 12 | 13 | namespace Core.Services.Filters 14 | { 15 | public class CustomExceptionFilter : IExceptionFilter 16 | { 17 | private readonly IAppSettings _apiSettings; 18 | private readonly ILogger _logger; 19 | public CustomExceptionFilter(IAppSettings apiSettings, ILoggerFactory loggerFactory) : base() 20 | { 21 | _apiSettings = apiSettings; 22 | _logger = loggerFactory.CreateLogger("ServiceLogFilter"); 23 | } 24 | 25 | public void OnException(ExceptionContext context) 26 | { 27 | var transactionName = "Core_Service"; 28 | if (context.ActionDescriptor!=null && context.ActionDescriptor.AttributeRouteInfo!=null) 29 | transactionName = context.ActionDescriptor.AttributeRouteInfo.Template; 30 | HttpResponse response = context.HttpContext.Response; 31 | response.StatusCode = (int)HttpStatusCode.InternalServerError; 32 | response.ContentType = "application/json"; 33 | var request = context.HttpContext.Request; 34 | var requestMethod = (request != null) ? request.Method : string.Empty; 35 | var message = context.Exception.Message + " StackTrace : " + context.Exception.StackTrace; 36 | Helpers.Helper.NewRelicLogging(transactionName, request.Path, requestMethod, string.Empty, response.StatusCode.ToString(), context.Exception, "Some Error has occured"); 37 | 38 | 39 | byte[] byteArray = Encoding.UTF8.GetBytes(message); 40 | 41 | using (var messageStream = new MemoryStream(byteArray)) 42 | { 43 | 44 | using (var stream = new StreamReader(messageStream)) 45 | { 46 | var responseBody = stream.ReadToEnd(); 47 | responseBody = responseBody.Replace(Environment.NewLine, ""); 48 | _logger.LogError($"@timestamp: {DateTime.Now},@site: {"core-service"}, @level: error, @threadid: {Thread.CurrentThread.ManagedThreadId}, @message: response -{responseBody}, requestUrl: {request.Path},requestMethod: {requestMethod}, responseCode: { response.StatusCode}"); 49 | } 50 | } 51 | 52 | var errorResponse = Helpers.Helper.ConvertToErrorResponse(context.Exception.Message, ErrorsType.UnhandledError.ToString(), ErrorMessageType.Error.ToString()); 53 | if (!_apiSettings.IsProd) 54 | { 55 | errorResponse.StackTrace = context.Exception.StackTrace; 56 | } 57 | 58 | context.Result = new ObjectResult(errorResponse); 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/Core.Services/Helpers/Helper.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Entities; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.IO; 5 | using System.Text; 6 | using System.Threading; 7 | 8 | namespace Core.Services.Helpers 9 | { 10 | public static class Helper 11 | { 12 | public static void LogInfo(ILogger logger,string transactionName,string request,string url,string requestMethodType,string statusCode,string response) 13 | { 14 | byte[] byteArray = Encoding.UTF8.GetBytes(request); 15 | using (var messageStream = new MemoryStream(byteArray)) 16 | { 17 | using (var stream = new StreamReader(messageStream)) 18 | { 19 | var requestInfo = stream.ReadToEnd(); 20 | requestInfo = requestInfo.Replace(Environment.NewLine, ""); 21 | NewRelicLogging(transactionName, url, requestMethodType, request, statusCode, null, response); 22 | logger.LogInformation($"@timestamp: {DateTime.Now},@site: {"core-service"}, @level: error, @threadid: {Thread.CurrentThread.ManagedThreadId}, @message: request -{requestInfo}, requestUrl: {url},requestMethod: {requestMethodType}, responseCode: { statusCode},response:{response}"); 23 | } 24 | } 25 | } 26 | 27 | public static void NewRelicLogging(string transactionName, string url,string requestMethod,string requestMessage,string statusCode, Exception exception,string responseMessage) 28 | { 29 | NewRelic.Api.Agent.NewRelic.SetTransactionName("Other", "/" + transactionName); 30 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("URL: ", url); 31 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("RequestType: ", requestMethod); 32 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("Request: ", requestMessage); 33 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("ResponseStatus: ", statusCode); 34 | 35 | if (exception!=null) 36 | { 37 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("Error: ", exception.Message); 38 | if(!string.IsNullOrEmpty(exception.StackTrace) ) 39 | { 40 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("StackTrace: ", exception.StackTrace); 41 | if ( exception.StackTrace.Length > 250) 42 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("StackTrace1: ", exception.StackTrace.Substring(250)); 43 | } 44 | } 45 | NewRelic.Api.Agent.NewRelic.AddCustomParameter("Response: ", responseMessage); 46 | } 47 | public static ResponseMessage ConvertToErrorResponse(string errorMessage, string messageId, string messageType) 48 | { 49 | ResponseMessage response = new ResponseMessage() 50 | { 51 | Message = errorMessage, 52 | FriendlyMessage = "Some error has occured....", 53 | MessageId = messageId, 54 | MessageType = messageType 55 | }; 56 | return response; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Core.Services/Logger/LogResponseMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Http.Internal; 3 | using Microsoft.Extensions.Logging; 4 | using System; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace Core.Services.Logger 13 | { 14 | public class LogResponseMiddleware 15 | { 16 | private readonly RequestDelegate _next; 17 | private readonly ILogger _logger; 18 | private Func _defaultFormatter = (state, exception) => state; 19 | 20 | public LogResponseMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) 21 | { 22 | _next = next; 23 | _logger = loggerFactory.CreateLogger("HyperLogFilter"); 24 | } 25 | 26 | public async Task Invoke(HttpContext context) 27 | { 28 | 29 | context.Request.EnableRewind(); 30 | var body = context.Request.Body; 31 | var buffer = new byte[Convert.ToInt32(context.Request.ContentLength)]; 32 | await context.Request.Body.ReadAsync(buffer, 0, buffer.Length); 33 | var requestBody = Encoding.UTF8.GetString(buffer); 34 | body.Seek(0, SeekOrigin.Begin); 35 | context.Request.Body = body; 36 | 37 | 38 | var bodyStream = context.Response.Body; 39 | var responseBodyStream = new MemoryStream(); 40 | context.Response.Body = responseBodyStream; 41 | 42 | await _next(context); 43 | 44 | responseBodyStream.Seek(0, SeekOrigin.Begin); 45 | var responseBody = new StreamReader(responseBodyStream).ReadToEnd(); 46 | 47 | var responseCode = context.Response.StatusCode; 48 | var requestpath = context.Request.Path; 49 | var requestMethod = context.Request.Method; 50 | if (responseCode == 200 || responseCode == 404 || responseCode == 400 || responseCode == 401) 51 | { 52 | var transactionName = "invoices/v1"; 53 | _logger.LogInformation($"@timestamp: {DateTime.Now},@site: {"hyper-invoices"}, @level: info, @threadid: {Thread.CurrentThread.ManagedThreadId}, @message: Logger Middleware response -{responseBody},requestBody:{requestBody}, requestUrl: {requestpath},requestMethod: {requestMethod}, responseCode: {responseCode}"); 54 | 55 | if (!string.IsNullOrEmpty(requestpath.Value) && requestpath.Value.Contains("/")) 56 | { 57 | var requestPaths = requestpath.Value.Split("/").Where(ee => !string.IsNullOrEmpty(ee)).Select(ee => Regex.Replace(ee, @"^\d+$", "{invoiceId}")); 58 | if(requestPaths.Any()) 59 | transactionName = String.Join("/", requestPaths.ToArray()); 60 | } 61 | Helpers.Helper.NewRelicLogging(transactionName, requestpath, requestMethod, requestBody, responseCode.ToString(), null, responseBody); 62 | } 63 | 64 | responseBodyStream.Seek(0, SeekOrigin.Begin); 65 | await responseBodyStream.CopyToAsync(bodyStream); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Core.Services/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace Core.Services 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | BuildWebHost(args).Run(); 11 | } 12 | 13 | public static IWebHost BuildWebHost(string[] args) => 14 | WebHost.CreateDefaultBuilder(args) 15 | .UseStartup() 16 | .Build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Core.Services/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52972/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "service/version", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "MicroserviceTemplate": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "launchUrl": "service/version", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "http://localhost:52973/" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Core.Services/Repositories/Database/DatabaseRepository.cs: -------------------------------------------------------------------------------- 1 | using Core.Services.Configurations; 2 | using Core.Services.Entities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Data.SqlClient; 7 | using System.Linq; 8 | 9 | namespace Core.Services.Repositories.Database 10 | { 11 | public class DatabaseRepository : IDatabaseRepository 12 | { 13 | private readonly IAppSettings _appSettings; 14 | 15 | public DatabaseRepository(IAppSettings appSettings) 16 | { 17 | _appSettings = appSettings; 18 | } 19 | 20 | #region Private Methods 21 | private string ConnectionString 22 | { 23 | get 24 | { 25 | return _appSettings.ConnectionString; 26 | } 27 | } 28 | private void CreateProcedureCommand(SqlCommand command, string procedureName, SqlConnection connection, SqlParameter[] dbParameters = null) 29 | { 30 | 31 | command.CommandText = procedureName; 32 | command.Connection = connection; 33 | command.CommandType = CommandType.StoredProcedure; 34 | if (dbParameters != null && dbParameters.Any()) 35 | command.Parameters.AddRange(dbParameters); 36 | } 37 | 38 | #endregion 39 | 40 | #region Public Methods 41 | public EmployeeDetail GetEmployeeDetailById(int employeeId) 42 | { 43 | EmployeeDetail employee = null; 44 | using (var connection = new SqlConnection(ConnectionString)) 45 | { 46 | if (connection.State == ConnectionState.Closed) 47 | connection.Open(); 48 | 49 | var sqlParameters = new List() { new SqlParameter("@empId", employeeId) }; 50 | using (var command = new SqlCommand()) 51 | { 52 | CreateProcedureCommand(command, "usp_GetEmployeeDetail", connection, sqlParameters.ToArray()); 53 | using (var rdr = command.ExecuteReader()) 54 | { 55 | while (rdr.Read()) 56 | { 57 | employee = new EmployeeDetail(); 58 | employee.EmployeeId = Convert.ToInt32(rdr["EmployeeId"]); 59 | employee.Name = Convert.ToString(rdr["Name"]); 60 | employee.Address = Convert.ToString(rdr["Address"]); 61 | employee.Salary = (!string.IsNullOrEmpty(Convert.ToString(rdr["Salary"]))) ? Convert.ToInt32(rdr["Salary"]) : 0; 62 | employee.DeptName = Convert.ToString(rdr["DeptName"]); 63 | employee.IsActive = Convert.ToBoolean(rdr["IsActive"]); 64 | } 65 | } 66 | } 67 | return employee; 68 | } 69 | } 70 | 71 | public List GetEmployeesList() 72 | { 73 | var employeeList = new List(); 74 | using (var connection = new SqlConnection(ConnectionString)) 75 | { 76 | if (connection.State == ConnectionState.Closed) 77 | connection.Open(); 78 | using (var command = new SqlCommand()) 79 | { 80 | CreateProcedureCommand(command, "usp_GetEmployeesList", connection, null); 81 | using (var rdr = command.ExecuteReader()) 82 | { 83 | while (rdr.Read()) 84 | { 85 | var employee = new Employee(); 86 | employee.EmployeeId = Convert.ToInt32(rdr["EMPLOYEEID"]); 87 | employee.Name = Convert.ToString(rdr["NAME"]); 88 | employee.DeptName = Convert.ToString(rdr["DeptName"]); 89 | employeeList.Add(employee); 90 | } 91 | } 92 | } 93 | return employeeList; 94 | } 95 | #endregion 96 | } 97 | public int AddEmployee(string name, string address, int salary, int departmentId) 98 | { 99 | int result=0; 100 | using (var connection = new SqlConnection(ConnectionString)) 101 | { 102 | if (connection.State == ConnectionState.Closed) 103 | connection.Open(); 104 | 105 | var sqlParameters = new List() { 106 | new SqlParameter("@name", name), 107 | new SqlParameter("@address", address), 108 | new SqlParameter("@salary", salary), 109 | new SqlParameter("@deptId", departmentId) 110 | }; 111 | using (var command = new SqlCommand()) 112 | { 113 | CreateProcedureCommand(command, "usp_AddEmployee", connection, sqlParameters.ToArray()); 114 | result = command.ExecuteNonQuery(); 115 | } 116 | return result; 117 | } 118 | } 119 | public int RemoveEmployee(int employeeId) 120 | { 121 | int result = 0; 122 | using (var connection = new SqlConnection(ConnectionString)) 123 | { 124 | if (connection.State == ConnectionState.Closed) 125 | connection.Open(); 126 | var sqlParameters = new List() { new SqlParameter("@empId", employeeId) }; 127 | 128 | using (var command = new SqlCommand()) 129 | { 130 | CreateProcedureCommand(command, "usp_DeleteEmployee", connection, sqlParameters.ToArray()); 131 | result = command.ExecuteNonQuery(); 132 | } 133 | return result; 134 | } 135 | } 136 | 137 | } 138 | } -------------------------------------------------------------------------------- /src/Core.Services/Repositories/Database/IDatabaseRepository.cs: -------------------------------------------------------------------------------- 1 |  2 | using Core.Services.Entities; 3 | using System.Collections.Generic; 4 | 5 | namespace Core.Services.Repositories.Database 6 | { 7 | public interface IDatabaseRepository 8 | { 9 | List GetEmployeesList(); 10 | EmployeeDetail GetEmployeeDetailById(int employeeId); 11 | int AddEmployee(string name, string address, int salary, int departmentId); 12 | int RemoveEmployee(int employeeId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Core.Services/Repositories/Models/DBErrorResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Hypergate.Invoices.Repositories.Models 3 | { 4 | public class DBErrorResponse 5 | { 6 | public string ErrorMessage { get; set; } 7 | public string MessageId { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Core.Services/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using Core.Services.Logger; 11 | using Core.Services.Filters; 12 | using Core.Services.Configurations; 13 | using Core.Services.Repositories.Database; 14 | 15 | namespace Core.Services 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | var appSettings = new AppSettings(); 30 | ConfigurationBinder.Bind(Configuration.GetSection("AppSettings"), appSettings); 31 | services.AddSingleton(appSettings); 32 | services.AddSingleton(); 33 | services.AddSingleton(); 34 | var loggerFactory = new LoggerFactory(); 35 | loggerFactory 36 | .WithFilter(new FilterLoggerSettings 37 | { 38 | { "Microsoft", Microsoft.Extensions.Logging.LogLevel.None }, 39 | { "System", Microsoft.Extensions.Logging.LogLevel.Warning }, 40 | { "HyperLogFilter", Microsoft.Extensions.Logging.LogLevel.Trace } 41 | }) 42 | .AddConsole(); 43 | loggerFactory.AddDebug(); 44 | services.AddMvc(_ => 45 | { 46 | _.Filters.Add(new CustomExceptionFilter(appSettings, loggerFactory)); 47 | }); 48 | } 49 | 50 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 51 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 52 | { 53 | loggerFactory 54 | .WithFilter(new FilterLoggerSettings 55 | { 56 | { "Microsoft",LogLevel.None }, 57 | { "System", LogLevel.Warning }, 58 | { "HyperLogFilter", LogLevel.Trace } 59 | }) 60 | .AddConsole(); 61 | loggerFactory.AddDebug(); 62 | app.UseMiddleware(); 63 | if (env.IsDevelopment()) 64 | { 65 | app.UseDeveloperExceptionPage(); 66 | } 67 | 68 | app.UseMvc(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Core.Services/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSettings": { 3 | "ApiKey": "67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Core.Services/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSettings": { 3 | "ApiKey": "be1367bc-bf77-49a6-8099-e8322ba9a52b", 4 | "IsProd": "true" 5 | } 6 | } -------------------------------------------------------------------------------- /src/Core.Services/appsettings.QA.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSettings": { 3 | "ApiKey": "67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Core.Services/appsettings.Staging.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSettings": { 3 | "ApiKey": "67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2" 4 | } 5 | } -------------------------------------------------------------------------------- /src/Core.Services/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSettings": { 3 | "ConnectionString": "data source=118.185.136.36;initial catalog=CoreServices;user id=sa;password=Tpg@1234;MultipleActiveResultSets=True;Connection Lifetime=0;Min Pool Size=0;Max Pool Size=100;Pooling=true;", 4 | "IsProd": "false", 5 | "AuthKeyName": "apikey", 6 | "ApiKey": "67cd910a-6a6d-4cb3-b57b-92f7a4dee9c2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/docker-compose.ci.build.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | ci-build: 5 | image: microsoft/aspnetcore-build:1.0-2.0 6 | volumes: 7 | - .:/src 8 | working_dir: /src 9 | command: /bin/bash -c "dotnet restore ./Core.Services.sln && dotnet publish ./Core.Services.sln -c Release -o ./obj/Docker/publish" 10 | -------------------------------------------------------------------------------- /src/docker-compose.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.0 5 | Linux 6 | fd57d91b-39f1-41d1-bb1b-34bf6bd90075 7 | True 8 | http://localhost:{ServicePort}/api/values 9 | microservicetemplate 10 | 11 | 12 | 13 | 14 | docker-compose.yml 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | microservicetemplate: 5 | environment: 6 | - ASPNETCORE_ENVIRONMENT=Development 7 | ports: 8 | - "18601:8601" 9 | -------------------------------------------------------------------------------- /src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | microservicetemplate: 5 | image: core.services 6 | build: 7 | context: ./Core.Services 8 | dockerfile: Dockerfile 9 | --------------------------------------------------------------------------------